diff --git a/wavplay_am824/.clang-format b/wavplay_am824/.clang-format new file mode 100644 index 0000000..ccfd9a9 --- /dev/null +++ b/wavplay_am824/.clang-format @@ -0,0 +1,157 @@ +--- +Language: Cpp +# BasedOnStyle: Chromium +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/wavplay_am824/CMakeLists.txt b/wavplay_am824/CMakeLists.txt new file mode 100644 index 0000000..cd0ce34 --- /dev/null +++ b/wavplay_am824/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.7.0) +project(wavplay_am824 CXX) +set(CMAKE_CXX_FLAGS "-g -Wall") +find_library(PORTAUDIO NAMES portaudio) +find_library(SNDFILE NAMES sndfile) +add_executable(wavplay_am824 wavplay_am824.cpp) +target_link_libraries(wavplay_am824 ${PORTAUDIO}) +target_link_libraries(wavplay_am824 ${SNDFILE}) diff --git a/wavplay_am824/README.md b/wavplay_am824/README.md new file mode 100644 index 0000000..49b809d --- /dev/null +++ b/wavplay_am824/README.md @@ -0,0 +1,12 @@ +# AM824 WAV player + +## Prerequisite ## +The player requires PortAudio and libsndfile libraries. +The [ubuntu-packages.sh](ubuntu-packages.sh) script can be used to install all the required packages on ubuntu distros. + +## Build ## +To build run: + + cmake . + make + diff --git a/wavplay_am824/am824_framer.h b/wavplay_am824/am824_framer.h index ccfd527..d0a41ed 100644 --- a/wavplay_am824/am824_framer.h +++ b/wavplay_am824/am824_framer.h @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,283 +15,254 @@ * along with this program. If not, see ************************************************************************************************************/ - #include #define CHANNEL_STATUS_BYTES 24 -#define WIDTH (8) +#define WIDTH (8) #define BOTTOMBIT 1 -#define REFLECTED_POLYNOMIAL 0xb8 // Unreflected is 0x1d +#define REFLECTED_POLYNOMIAL 0xb8 // Unreflected is 0x1d -enum AM824ErrorCode{ AM824_ERR_OK = 0, AM824_ERR_BAD_SAMPLING_FREQUENCY = -1, AM824_ERR_UNSUPPORTED_BITDEPTH = -2 }; +enum AM824ErrorCode { + AM824_ERR_OK = 0, + AM824_ERR_BAD_SAMPLING_FREQUENCY = -1, + AM824_ERR_UNSUPPORTED_BITDEPTH = -2 +}; -enum AM824SamplingFrequency { FS_NOT_INDICATED = 0, FS_44100_HZ = 1, FS_48000_HZ = 2, FS_32000_HZ = 3 }; +enum AM824SamplingFrequency { + FS_NOT_INDICATED = 0, + FS_44100_HZ = 1, + FS_48000_HZ = 2, + FS_32000_HZ = 3 +}; enum AM824Endianess { AM824_BIG_ENDIAN, AM824_LITTLE_ENDIAN }; -class AM824Framer -{ - uint8_t channelStatusIndex; - uint8_t channelStatusMask; - uint8_t channelStatus[CHANNEL_STATUS_BYTES]; - uint8_t subFrameCounter; - uint8_t numChannels; - uint8_t bitDepth; - uint8_t crcTable[256]; - AM824Endianess endian; +class AM824Framer { + uint8_t channelStatusIndex; + uint8_t channelStatusMask; + uint8_t channelStatus[CHANNEL_STATUS_BYTES]; + uint8_t subFrameCounter; + uint8_t numChannels; + uint8_t bitDepth; + uint8_t crcTable[256]; + AM824Endianess endian; - static uint8_t getParity(unsigned int n) - { - uint8_t parity = 0; - while (n) - { - parity = 1 - parity; - n = n & (n - 1); - } - return parity; - } + static uint8_t getParity(unsigned int n) { + uint8_t parity = 0; + while (n) { + parity = 1 - parity; + n = n & (n - 1); + } + return parity; + } - void crcTableInit(void) - { - uint8_t remainder; + void crcTableInit(void) { + uint8_t remainder; - for (int dividend = 0; dividend < 256; ++dividend) - { - remainder = dividend << (WIDTH - 8); - for (uint8_t bit = 0; bit < 8; bit++) - { - if (remainder & BOTTOMBIT) - { - remainder = (remainder >> 1) ^ REFLECTED_POLYNOMIAL; - } - else - { - remainder = (remainder >> 1); - } - } - crcTable[dividend] = remainder; - } + for (int dividend = 0; dividend < 256; ++dividend) { + remainder = dividend << (WIDTH - 8); + for (uint8_t bit = 0; bit < 8; bit++) { + if (remainder & BOTTOMBIT) { + remainder = (remainder >> 1) ^ REFLECTED_POLYNOMIAL; + } else { + remainder = (remainder >> 1); + } + } + crcTable[dividend] = remainder; + } + } - } + void setCRC(void) { + uint8_t data; + uint8_t remainder = 0xff; - void setCRC(void) - { - uint8_t data; - uint8_t remainder = 0xff; + for (int byte = 0; byte < 23; byte++) { + data = channelStatus[byte] ^ remainder; + remainder = crcTable[data]; + } + channelStatus[23] = remainder; + } - for (int byte = 0; byte < 23; byte++) - { - data = channelStatus[byte] ^ remainder; - remainder = crcTable[data]; - } - channelStatus[23] = remainder; - } + public: + // Input number of channels and the bitdepth of the input samples + // Note that the output bit depth is always 24 bit + AM824Framer( + uint8_t + newNumChannels, /* Input - Number of channels of input/output audio */ + uint8_t newBitDepth, /* Input - Bit depth of input audio, output is always + 32 bit */ + AM824Endianess outputEndianess, /* Input = Endianess of output samples, + input is always machine order */ + AM824ErrorCode& err) /* Output - error code */ + { + uint8_t i; + channelStatusIndex = 0; + channelStatusMask = 1; + numChannels = newNumChannels; + subFrameCounter = 0; + bitDepth = newBitDepth; + endian = outputEndianess; + // Set certain channel status bits + // Clear it first + for (i = 0; i < CHANNEL_STATUS_BYTES; i++) { + channelStatus[i] = 0; + } + // Default Channel Status + // Professional Bit + channelStatus[0] |= 1; + // Non-audio PCM + channelStatus[0] |= 1 << 1; + // 48kHz + channelStatus[0] |= 2 << 6; -public: + switch (bitDepth) { + case 16: + channelStatus[2] |= 1 << 3; + break; + case 20: + // Use of Auxillary bits + channelStatus[2] |= 4; + // 20 bit data + channelStatus[2] |= 1 << 3; + break; + case 24: + // Use of Auxillary bits + channelStatus[2] |= 4; + // 24 bit data + channelStatus[2] |= 5 << 3; + break; + default: + err = AM824_ERR_UNSUPPORTED_BITDEPTH; + return; + } + crcTableInit(); + setCRC(); + err = AM824_ERR_OK; + } - // Input number of channels and the bitdepth of the input samples - // Note that the output bit depth is always 24 bit - AM824Framer(uint8_t newNumChannels, /* Input - Number of channels of input/output audio */ - uint8_t newBitDepth, /* Input - Bit depth of input audio, output is always 32 bit */ - AM824Endianess outputEndianess, /* Input = Endianess of output samples, input is always machine order */ - AM824ErrorCode &err) /* Output - error code */ - { - uint8_t i; - channelStatusIndex = 0; - channelStatusMask = 1; - numChannels = newNumChannels; - subFrameCounter = 0; + AM824ErrorCode setSamplingFrequency(AM824SamplingFrequency fs_code) { + if (fs_code > FS_32000_HZ) { + return (AM824_ERR_BAD_SAMPLING_FREQUENCY); + } + // Reset top two bits and set accordingly + channelStatus[0] &= 0x3f; + channelStatus[0] |= fs_code << 6; + setCRC(); + return (AM824_ERR_OK); + } - bitDepth = newBitDepth; - endian = outputEndianess; - // Set certain channel status bits - // Clear it first - for (i = 0 ; i < CHANNEL_STATUS_BYTES ; i++) - { - channelStatus[i] = 0; - } - // Default Channel Status - // Professional Bit - channelStatus[0] |= 1; - // Non-audio PCM - channelStatus[0] |= 1 << 1; - // 48kHz - channelStatus[0] |= 2 << 6; + void setProfessionalMode(void) { + channelStatus[0] |= 1; + setCRC(); + } - switch(bitDepth) - { - case 16: - channelStatus[2] |= 1 << 3; - break; - case 20: - // Use of Auxillary bits - channelStatus[2] |= 4; - // 20 bit data - channelStatus[2] |= 1 << 3; - break; - case 24: - // Use of Auxillary bits - channelStatus[2] |= 4; - // 24 bit data - channelStatus[2] |= 5 << 3; - break; - default: - err = AM824_ERR_UNSUPPORTED_BITDEPTH; - return; - } - crcTableInit(); - setCRC(); - err = AM824_ERR_OK; - } + void setConsumerMode(void) { + channelStatus[0] &= 0xfe; + setCRC(); + } - AM824ErrorCode setSamplingFrequency(AM824SamplingFrequency fs_code) - { - if (fs_code > FS_32000_HZ) - { - return(AM824_ERR_BAD_SAMPLING_FREQUENCY); - } - // Reset top two bits and set accordingly - channelStatus[0] &= 0x3f; - channelStatus[0] |= fs_code << 6; - setCRC(); - return(AM824_ERR_OK); - } + void setAudioMode(void) { + channelStatus[0] &= 0xfd; + setCRC(); + } - void setProfessionalMode(void) - { - channelStatus[0] |= 1; - setCRC(); - } + void setDataMode(void) { + channelStatus[0] |= 2; + setCRC(); + } - void setConsumerMode(void) - { - channelStatus[0] &= 0xfe; - setCRC(); - } + void getAM824Sample(uint32_t inputSample, uint8_t* outputBytes) { + uint32_t outputSample; + bool channelStatusBit = + channelStatus[channelStatusIndex] & channelStatusMask; + bool userBit = false; + bool validityBit = false; - void setAudioMode(void) - { - channelStatus[0] &= 0xfd; - setCRC(); - } + // Input samples are MSB justified as per AES3 + if (bitDepth == 16) { + outputSample = inputSample << 8; + } else { + outputSample = inputSample; + } - void setDataMode(void) - { - channelStatus[0] |= 2; - setCRC(); - } + // Detect block start + if ((channelStatusIndex == 0) && (channelStatusMask == 1)) { + outputSample |= 1 << 29; + } + // Detect frame start + if (subFrameCounter == 0) { + outputSample |= 1 << 28; + } - void getAM824Sample(uint32_t inputSample, uint8_t *outputBytes) - { - uint32_t outputSample; - bool channelStatusBit = channelStatus[channelStatusIndex] & channelStatusMask; - bool userBit = false; - bool validityBit = false; + outputSample |= channelStatusBit << 26; + outputSample |= userBit << 25; + outputSample |= validityBit << 24; - // Input samples are MSB justified as per AES3 - if (bitDepth == 16) - { - outputSample = inputSample << 8; - } - else - { - outputSample = inputSample; - } + // Now complete except for parity so calculate that + outputSample |= getParity(outputSample) << 27; - // Detect block start - if ((channelStatusIndex == 0) && (channelStatusMask == 1)) - { - outputSample |= 1 << 29; - } - // Detect frame start - if (subFrameCounter == 0) - { - outputSample |= 1 << 28; - } - - outputSample |= channelStatusBit << 26; - outputSample |= userBit << 25; - outputSample |= validityBit << 24; - - // Now complete except for parity so calculate that - outputSample |= getParity(outputSample) << 27; - - // Now complete all the wraparound checks - // Note that channel status chan be different for the different subframes (channels) - // but in this example channel status is set to be the same for all subframes (channels) - subFrameCounter++; - if (subFrameCounter == numChannels) - { - subFrameCounter = 0; - // Move to next channel status bit - if (channelStatusMask == 0x80) - { - channelStatusMask = 1; - channelStatusIndex++; - if (channelStatusIndex == CHANNEL_STATUS_BYTES) - { - channelStatusIndex = 0; - } - } - else - { - channelStatusMask = channelStatusMask << 1; - } - } - if (endian == AM824_BIG_ENDIAN) - { - // Return in 32 bit Big Endian format - *outputBytes++ = (outputSample & 0xff000000) >> 24; - *outputBytes++ = (outputSample & 0x00ff0000) >> 16; - *outputBytes++ = (outputSample & 0x0000ff00) >> 8; - *outputBytes++ = (outputSample & 0x000000ff) >> 0; - } - else - { - // Return in 32 bit Little Endian format - *outputBytes++ = (outputSample & 0x000000ff) >> 0; - *outputBytes++ = (outputSample & 0x0000ff00) >> 8; - *outputBytes++ = (outputSample & 0x00ff0000) >> 16; - *outputBytes++ = (outputSample & 0xff000000) >> 24; - } - } - - /* Simple test code to check CRC implementation */ - /* See EBU Tech 3250 or AES3 for the reference for these examples */ - void testCRC(void) - { - unsigned int i; - for (i = 0 ; i < CHANNEL_STATUS_BYTES ; i++) - { - channelStatus[i] = 0; - } - // From AES3-2-2009-r2019 - Example 1 - channelStatus[0] = 0x3d; - channelStatus[1] = 2; - channelStatus[4] = 2; - setCRC(); - if (channelStatus[23] == 0x9b) - { - printf("Example 1 - passed\n"); - } - else - { - printf("Example 1 - failed, expecting 0x9b, got 0x%x\n",channelStatus[23]); - } - channelStatus[0] = 0x01; - channelStatus[1] = 0; - channelStatus[4] = 0; - setCRC(); - if (channelStatus[23] == 0x32) - { - printf("Example 2 - passed\n"); - } - else - { - printf("Example 2 - failed, expecting 0x32, got 0x%x\n",channelStatus[23]); - } - } + // Now complete all the wraparound checks + // Note that channel status chan be different for the different subframes + // (channels) but in this example channel status is set to be the same for + // all subframes (channels) + subFrameCounter++; + if (subFrameCounter == numChannels) { + subFrameCounter = 0; + // Move to next channel status bit + if (channelStatusMask == 0x80) { + channelStatusMask = 1; + channelStatusIndex++; + if (channelStatusIndex == CHANNEL_STATUS_BYTES) { + channelStatusIndex = 0; + } + } else { + channelStatusMask = channelStatusMask << 1; + } + } + if (endian == AM824_BIG_ENDIAN) { + // Return in 32 bit Big Endian format + *outputBytes++ = (outputSample & 0xff000000) >> 24; + *outputBytes++ = (outputSample & 0x00ff0000) >> 16; + *outputBytes++ = (outputSample & 0x0000ff00) >> 8; + *outputBytes++ = (outputSample & 0x000000ff) >> 0; + } else { + // Return in 32 bit Little Endian format + *outputBytes++ = (outputSample & 0x000000ff) >> 0; + *outputBytes++ = (outputSample & 0x0000ff00) >> 8; + *outputBytes++ = (outputSample & 0x00ff0000) >> 16; + *outputBytes++ = (outputSample & 0xff000000) >> 24; + } + } + /* Simple test code to check CRC implementation */ + /* See EBU Tech 3250 or AES3 for the reference for these examples */ + void testCRC(void) { + unsigned int i; + for (i = 0; i < CHANNEL_STATUS_BYTES; i++) { + channelStatus[i] = 0; + } + // From AES3-2-2009-r2019 - Example 1 + channelStatus[0] = 0x3d; + channelStatus[1] = 2; + channelStatus[4] = 2; + setCRC(); + if (channelStatus[23] == 0x9b) { + printf("Example 1 - passed\n"); + } else { + printf("Example 1 - failed, expecting 0x9b, got 0x%x\n", + channelStatus[23]); + } + channelStatus[0] = 0x01; + channelStatus[1] = 0; + channelStatus[4] = 0; + setCRC(); + if (channelStatus[23] == 0x32) { + printf("Example 2 - passed\n"); + } else { + printf("Example 2 - failed, expecting 0x32, got 0x%x\n", + channelStatus[23]); + } + } }; \ No newline at end of file diff --git a/wavplay_am824/bin/Linux/wavplay_am824 b/wavplay_am824/bin/Linux/wavplay_am824 deleted file mode 100644 index c6d935a..0000000 Binary files a/wavplay_am824/bin/Linux/wavplay_am824 and /dev/null differ diff --git a/wavplay_am824/ubuntu-packages.sh b/wavplay_am824/ubuntu-packages.sh new file mode 100755 index 0000000..2b23166 --- /dev/null +++ b/wavplay_am824/ubuntu-packages.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# +# Tested on Ubuntu 18.04 +# + +sudo apt update +sudo apt-get install -y libsndfile1-dev +sudo apt-get install -y libportaudio2 +sudo apt-get install -y sox diff --git a/wavplay_am824/wavplay_am824.cpp b/wavplay_am824/wavplay_am824.cpp index 3f12741..1d54f7e 100644 --- a/wavplay_am824/wavplay_am824.cpp +++ b/wavplay_am824/wavplay_am824.cpp @@ -5,7 +5,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,7 @@ #include "portaudio.h" #include "am824_framer.h" -#if defined(__linux__ ) +#if defined(__linux__) #include "pa_linux_alsa.h" #endif @@ -43,858 +43,766 @@ #include #endif -typedef enum -{ - FF_PCM = 0, - FF_FLOAT32, -} -FileFormat; +typedef enum { + FF_PCM = 0, + FF_FLOAT32, +} FileFormat; +typedef enum { + ERR_FILE_NOT_FOUND = -101, + ERR_BAD_INPUT_FILE, + ERR_BAD_WAV_FORMAT, + ERR_INCOMPLETE_INPUT_FILE, + ERR_NOT_SUPPORTED, + ERR_BAD_CMD_OPTION, + ERR_NO_MEMORY, + ERR_NO_DEVICE, + ERR_NO_STREAM, + ERR_PORTAUDIO, + ERR_OK = 0 +} ErrorCode; -typedef enum -{ - ERR_FILE_NOT_FOUND = -101, - ERR_BAD_INPUT_FILE, - ERR_BAD_WAV_FORMAT, - ERR_INCOMPLETE_INPUT_FILE, - ERR_NOT_SUPPORTED, - ERR_BAD_CMD_OPTION, - ERR_NO_MEMORY, - ERR_NO_DEVICE, - ERR_NO_STREAM, - ERR_PORTAUDIO, - ERR_OK = 0 -} -ErrorCode; - - -typedef struct -{ - unsigned long frameIndex; /* Index into sample array. */ - unsigned long maxFrameIndex; - unsigned int numChannels; - unsigned int bytesPerSample; - unsigned char *audio; - FileFormat waveFileFormat; - unsigned int bitsPerSample; - unsigned int fs; - unsigned int blockAlign; - unsigned long totalBytes; +typedef struct { + unsigned long frameIndex; /* Index into sample array. */ + unsigned long maxFrameIndex; + unsigned int numChannels; + unsigned int bytesPerSample; + unsigned char* audio; + FileFormat waveFileFormat; + unsigned int bitsPerSample; + unsigned int fs; + unsigned int blockAlign; + unsigned long totalBytes; #if defined(_WIN32) || defined(_WIN64) - HMMIO waveFile; + HMMIO waveFile; #else - SNDFILE *waveFile; + SNDFILE* waveFile; #endif - unsigned int am824_audio; - AM824SamplingFrequency am824_fs; - unsigned int am824_fs_match_wavfile; - unsigned int am824_professional; -} -UserData; + unsigned int am824_audio; + AM824SamplingFrequency am824_fs; + unsigned int am824_fs_match_wavfile; + unsigned int am824_professional; +} UserData; -void throwError(ErrorCode err, const char *format, ...) -{ - va_list args; - va_start(args, format); +void throwError(ErrorCode err, const char* format, ...) { + va_list args; + va_start(args, format); - fprintf(stderr, "***Error: "); - vfprintf(stderr, format, args); - va_end(args); - fprintf(stderr, "\n"); - fflush(stderr); - exit(err); + fprintf(stderr, "***Error: "); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + fflush(stderr); + exit(err); } -/* This is the main callback functioned called by PortAudio. It is registered when the stream is created */ +/* This is the main callback functioned called by PortAudio. It is registered + * when the stream is created */ -static int playCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - UserData *data = (UserData*)userData; - unsigned char *rptr = &data->audio[data->frameIndex * data->numChannels * data->bytesPerSample]; - unsigned char *wptr = (unsigned char *)outputBuffer; - unsigned int i; - int finished; - unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; +static int playCallback(const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) { + UserData* data = (UserData*)userData; + unsigned char* rptr = + &data->audio[data->frameIndex * data->numChannels * data->bytesPerSample]; + unsigned char* wptr = (unsigned char*)outputBuffer; + unsigned int i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; - (void) inputBuffer; /* Prevent unused variable warnings. */ - (void) timeInfo; - (void) statusFlags; - (void) userData; + (void)inputBuffer; /* Prevent unused variable warnings. */ + (void)timeInfo; + (void)statusFlags; + (void)userData; - if( framesLeft < framesPerBuffer ) - { - /* final buffer... */ - for( i=0; inumChannels * data->bytesPerSample; i++) - { - *wptr++ = *rptr++; - } - for( ; inumChannels * data->bytesPerSample; i++ ) - { - *wptr++ = 0; - } - data->frameIndex += framesLeft; - finished = paComplete; + if (framesLeft < framesPerBuffer) { + /* final buffer... */ + for (i = 0; i < framesLeft * data->numChannels * data->bytesPerSample; + i++) { + *wptr++ = *rptr++; } - else - { - for( i=0; i< framesPerBuffer * data->numChannels * data->bytesPerSample; i++) - { - *wptr++ = *rptr++; - } - data->frameIndex += framesPerBuffer; - finished = paContinue; + for (; i < framesPerBuffer * data->numChannels * data->bytesPerSample; + i++) { + *wptr++ = 0; } - return finished; + data->frameIndex += framesLeft; + finished = paComplete; + } else { + for (i = 0; i < framesPerBuffer * data->numChannels * data->bytesPerSample; + i++) { + *wptr++ = *rptr++; + } + data->frameIndex += framesPerBuffer; + finished = paContinue; + } + return finished; } -/* Two versions of the following two functions exist, one for Windows and one for Linux. This approach was chosen - because a platform independent wav file library was not used but rather Windows API and libasound directly */ +/* Two versions of the following two functions exist, one for Windows and one + for Linux. This approach was chosen because a platform independent wav file + library was not used but rather Windows API and libasound directly */ #if defined(_WIN32) || defined(_WIN64) -int read_wav_file_header(char *playbackWaveFile, /* Input file name string */ - UserData *outputData) /* Output options */ +int read_wav_file_header(char* playbackWaveFile, /* Input file name string */ + UserData* outputData) /* Output options */ { - MMCKINFO mmckinfoParent; - MMCKINFO mmckinfoSubchunk; - WAVEFORMATEXTENSIBLE *format; + MMCKINFO mmckinfoParent; + MMCKINFO mmckinfoSubchunk; + WAVEFORMATEXTENSIBLE* format; - outputData->waveFile = mmioOpenA(playbackWaveFile, 0, MMIO_READ | MMIO_ALLOCBUF); - if (!outputData->waveFile) - { - throwError(ERR_FILE_NOT_FOUND, "Can't Open %s!", playbackWaveFile); - } - mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); - if (mmioDescend(outputData->waveFile, (LPMMCKINFO)&mmckinfoParent, 0, MMIO_FINDRIFF)) - { - throwError(ERR_BAD_INPUT_FILE, "This file doesn't contain a WAVE!"); - } - mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); - if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) - { - throwError(ERR_BAD_WAV_FORMAT, "Required fmt chunk was not found!"); - } + outputData->waveFile = + mmioOpenA(playbackWaveFile, 0, MMIO_READ | MMIO_ALLOCBUF); + if (!outputData->waveFile) { + throwError(ERR_FILE_NOT_FOUND, "Can't Open %s!", playbackWaveFile); + } + mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); + if (mmioDescend(outputData->waveFile, (LPMMCKINFO)&mmckinfoParent, 0, + MMIO_FINDRIFF)) { + throwError(ERR_BAD_INPUT_FILE, "This file doesn't contain a WAVE!"); + } + mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); + if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, + MMIO_FINDCHUNK)) { + throwError(ERR_BAD_WAV_FORMAT, "Required fmt chunk was not found!"); + } - format = (WAVEFORMATEXTENSIBLE *)malloc(mmckinfoSubchunk.cksize); + format = (WAVEFORMATEXTENSIBLE*)malloc(mmckinfoSubchunk.cksize); - - if (mmioRead(outputData->waveFile, (HPSTR)format, mmckinfoSubchunk.cksize) != (LRESULT)mmckinfoSubchunk.cksize) - { - throwError(ERR_BAD_WAV_FORMAT, "Reading the fmt chunk!"); - } + if (mmioRead(outputData->waveFile, (HPSTR)format, mmckinfoSubchunk.cksize) != + (LRESULT)mmckinfoSubchunk.cksize) { + throwError(ERR_BAD_WAV_FORMAT, "Reading the fmt chunk!"); + } - if ((format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && - (format->Samples.wValidBitsPerSample != format->Format.wBitsPerSample)) - { - throwError(ERR_NOT_SUPPORTED, "Different container size and bit depth not supported"); - } + if ((format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && + (format->Samples.wValidBitsPerSample != format->Format.wBitsPerSample)) { + throwError(ERR_NOT_SUPPORTED, + "Different container size and bit depth not supported"); + } - mmioAscend(outputData->waveFile, &mmckinfoSubchunk, 0); - mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); - if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) - { - throwError(ERR_BAD_WAV_FORMAT, "Reading the data chunk!"); - } + mmioAscend(outputData->waveFile, &mmckinfoSubchunk, 0); + mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); + if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, + MMIO_FINDCHUNK)) { + throwError(ERR_BAD_WAV_FORMAT, "Reading the data chunk!"); + } - outputData->fs = format->Format.nSamplesPerSec; - outputData->numChannels = format->Format.nChannels; - outputData->totalBytes = mmckinfoSubchunk.cksize; - outputData->bitsPerSample = format->Format.wBitsPerSample; - outputData->bytesPerSample = outputData->bitsPerSample / 8; - outputData->blockAlign = format->Format.nBlockAlign; + outputData->fs = format->Format.nSamplesPerSec; + outputData->numChannels = format->Format.nChannels; + outputData->totalBytes = mmckinfoSubchunk.cksize; + outputData->bitsPerSample = format->Format.wBitsPerSample; + outputData->bytesPerSample = outputData->bitsPerSample / 8; + outputData->blockAlign = format->Format.nBlockAlign; - if (format->Format.wFormatTag == WAVE_FORMAT_PCM) - { - outputData->waveFileFormat = FF_PCM; - } - else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) - { - outputData->waveFileFormat = FF_FLOAT32; - } - else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) - { - if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) - { - outputData->waveFileFormat = FF_PCM; - } - else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) - { - outputData->waveFileFormat == FF_FLOAT32; - } - else - { - throwError(ERR_NOT_SUPPORTED, "Error: Unsupported WAVEFORMAT EXTENSIBLE SUBTYPE!"); - } - } + if (format->Format.wFormatTag == WAVE_FORMAT_PCM) { + outputData->waveFileFormat = FF_PCM; + } else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + outputData->waveFileFormat = FF_FLOAT32; + } else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { + outputData->waveFileFormat = FF_PCM; + } else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { + outputData->waveFileFormat == FF_FLOAT32; + } else { + throwError(ERR_NOT_SUPPORTED, + "Error: Unsupported WAVEFORMAT EXTENSIBLE SUBTYPE!"); + } + } - return(0); + return (0); } -unsigned long read_entire_wav_file(UserData *outputData, /* Input options*/ - void *audioData) /* Output data read form file */ +unsigned long read_entire_wav_file( + UserData* outputData, /* Input options*/ + void* audioData) /* Output data read form file */ { - unsigned long readCount; + unsigned long readCount; - readCount = mmioRead(outputData->waveFile, (char *)audioData, outputData->totalBytes); + readCount = + mmioRead(outputData->waveFile, (char*)audioData, outputData->totalBytes); - mmioClose(outputData->waveFile, 0); + mmioClose(outputData->waveFile, 0); - if (readCount != outputData->totalBytes) - { - throwError(ERR_INCOMPLETE_INPUT_FILE, "Failed to read all of audio data in wave file"); - } - return(readCount); + if (readCount != outputData->totalBytes) { + throwError(ERR_INCOMPLETE_INPUT_FILE, + "Failed to read all of audio data in wave file"); + } + return (readCount); } #else -int read_wav_file_header(char *playbackWaveFile, /* Input filename string */ - UserData *outputData) /* Output options */ +int read_wav_file_header(char* playbackWaveFile, /* Input filename string */ + UserData* outputData) /* Output options */ { - uint32_t waveFormat; - unsigned int i; - uint8_t tmpByte; - SF_INFO fileInfo; - unsigned long long readCount; - unsigned char *pByte; + uint32_t waveFormat; + SF_INFO fileInfo; - outputData->waveFile = sf_open(playbackWaveFile, SFM_READ, &fileInfo); - if (!outputData->waveFile) - { - throwError(ERR_FILE_NOT_FOUND, "File %s not found\n", playbackWaveFile); - } + outputData->waveFile = sf_open(playbackWaveFile, SFM_READ, &fileInfo); + if (!outputData->waveFile) { + throwError(ERR_FILE_NOT_FOUND, "File %s not found\n", playbackWaveFile); + } - outputData->fs = fileInfo.samplerate; - outputData->numChannels = fileInfo.channels; + outputData->fs = fileInfo.samplerate; + outputData->numChannels = fileInfo.channels; - if ((waveFormat & SF_FORMAT_TYPEMASK) == SF_FORMAT_RF64) - { - throwError(ERR_NOT_SUPPORTED, "RF64 format not yet supported"); - } + waveFormat = fileInfo.format; + if ((waveFormat & SF_FORMAT_TYPEMASK) == SF_FORMAT_RF64) { + throwError(ERR_NOT_SUPPORTED, "RF64 format not yet supported"); + } - waveFormat = fileInfo.format; - if (((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) && - ((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX)) - { - throwError(ERR_BAD_INPUT_FILE, "Input file is not a wavefile"); - } + if (((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) && + ((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX)) { + throwError(ERR_BAD_INPUT_FILE, "Input file is not a wavefile"); + } - switch(waveFormat & SF_FORMAT_SUBMASK) - { - case SF_FORMAT_PCM_16: - outputData->waveFileFormat = FF_PCM; - outputData->bitsPerSample = 16; - break; - case SF_FORMAT_PCM_24: - outputData->waveFileFormat = FF_PCM; - outputData->bitsPerSample = 24; - break; - case SF_FORMAT_PCM_32: - outputData->waveFileFormat = FF_PCM; - outputData->bitsPerSample = 32; - break; - case SF_FORMAT_FLOAT: - outputData->waveFileFormat = FF_FLOAT32; - outputData->bitsPerSample = 32; - break; - default: - throwError(ERR_NOT_SUPPORTED, "Unsupported wavefile format supported"); - } + switch (waveFormat & SF_FORMAT_SUBMASK) { + case SF_FORMAT_PCM_16: + outputData->waveFileFormat = FF_PCM; + outputData->bitsPerSample = 16; + break; + case SF_FORMAT_PCM_24: + outputData->waveFileFormat = FF_PCM; + outputData->bitsPerSample = 24; + break; + case SF_FORMAT_PCM_32: + outputData->waveFileFormat = FF_PCM; + outputData->bitsPerSample = 32; + break; + case SF_FORMAT_FLOAT: + outputData->waveFileFormat = FF_FLOAT32; + outputData->bitsPerSample = 32; + break; + default: + throwError(ERR_NOT_SUPPORTED, "Unsupported wavefile format supported"); + } - outputData->bytesPerSample = outputData->bitsPerSample / 8; - outputData->blockAlign = outputData->numChannels * outputData->bytesPerSample; - outputData->totalBytes = outputData->blockAlign * fileInfo.frames; + outputData->bytesPerSample = outputData->bitsPerSample / 8; + outputData->blockAlign = outputData->numChannels * outputData->bytesPerSample; + outputData->totalBytes = outputData->blockAlign * fileInfo.frames; - return(0); + return (0); } -unsigned long read_entire_wav_file(UserData *outputData, /* Input Options */ - void *audioData) /* Output Audio data from file */ +unsigned long read_entire_wav_file( + UserData* outputData, /* Input Options */ + void* audioData) /* Output Audio data from file */ { - unsigned long readCount; + unsigned long readCount; - readCount = sf_read_raw(outputData->waveFile, audioData, outputData->totalBytes); - if (readCount != outputData->totalBytes) - { - throwError(ERR_INCOMPLETE_INPUT_FILE, "Failed to read all of audio data in wave file"); - } - return(readCount); + readCount = + sf_read_raw(outputData->waveFile, audioData, outputData->totalBytes); + if (readCount != outputData->totalBytes) { + throwError(ERR_INCOMPLETE_INPUT_FILE, + "Failed to read all of audio data in wave file"); + } + return (readCount); } - #endif - - -unsigned int countBits(unsigned int a) -{ - unsigned int count = 0; - while (a) - { - count += (a & 0x1); - a >>= 1; - } - return(count); +unsigned int countBits(unsigned int a) { + unsigned int count = 0; + while (a) { + count += (a & 0x1); + a >>= 1; + } + return (count); } -/* This function takes standard PCM audio plus channel status options and creates the samples for AM824 format */ +/* This function takes standard PCM audio plus channel status options and + * creates the samples for AM824 format */ -void am824Convert(UserData *userData, /* Input options */ - void *audio, /* Input PCM samples */ - void **am824audio) /* Output AM824 samples, always 32bit */ +void am824Convert(UserData* userData, /* Input options */ + void* audio, /* Input PCM samples */ + void** am824audio) /* Output AM824 samples, always 32bit */ { - unsigned long bytesConverted = 0; - uint32_t inputSample32; - uint16_t inputSample16; - uint8_t *inputPtr; - uint32_t *outputPtr; - unsigned int channel; - unsigned long outputMemSize; - AM824ErrorCode err; + unsigned long bytesConverted = 0; + uint32_t inputSample32; + uint16_t inputSample16; + uint8_t* inputPtr; + uint32_t* outputPtr; + unsigned int channel; + unsigned long outputMemSize; + AM824ErrorCode err; + if ((userData->bytesPerSample != 3) && (userData->bytesPerSample != 2)) { + throwError(ERR_NOT_SUPPORTED, + "Only 2 or 3 bytes per sample supported for AM824 mode"); + } - if ((userData->bytesPerSample != 3) && (userData->bytesPerSample != 2)) - { - throwError(ERR_NOT_SUPPORTED, "Only 2 or 3 bytes per sample supported for AM824 mode"); - } + AM824Framer framer(userData->numChannels, userData->bytesPerSample * 8, + AM824_LITTLE_ENDIAN, err); + if (err != AM824_ERR_OK) { + if (err == AM824_ERR_UNSUPPORTED_BITDEPTH) { + throwError(ERR_NOT_SUPPORTED, + "AM824 framer reports bitdepth %d not supported", + userData->bytesPerSample * 8); + } + } - AM824Framer framer(userData->numChannels, userData->bytesPerSample * 8, AM824_LITTLE_ENDIAN, err); - if (err != AM824_ERR_OK) - { - if (err == AM824_ERR_UNSUPPORTED_BITDEPTH) - { - throwError(ERR_NOT_SUPPORTED, "AM824 framer reports bitdepth %d not supported", userData->bytesPerSample * 8); - } - } + // framer.testCRC(); - //framer.testCRC(); - - if (userData->am824_audio) - { - framer.setAudioMode(); - } - else - { - framer.setDataMode(); - } - if (userData->am824_fs_match_wavfile) - { - switch(userData->fs) - { - case 32000: - framer.setSamplingFrequency(FS_32000_HZ); - break; - case 44100: - framer.setSamplingFrequency(FS_44100_HZ); - break; - case 48000: - framer.setSamplingFrequency(FS_48000_HZ); - break; - default: - framer.setSamplingFrequency(FS_NOT_INDICATED); - } - } - else - { - framer.setSamplingFrequency(userData->am824_fs); - } - if (userData->am824_professional) - { - framer.setProfessionalMode(); - } - else - { - framer.setConsumerMode(); - } - outputMemSize = (userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample; - *am824audio = malloc(outputMemSize); - outputPtr = (uint32_t *) *am824audio; - inputPtr = (uint8_t *)audio; - - while(bytesConverted < userData->totalBytes) - { - for (channel = 0 ; channel < userData->numChannels ; channel++) - { - if (userData->bytesPerSample == 3) - { - inputSample32 = *((uint32_t *)inputPtr) & 0xffffff; - framer.getAM824Sample(inputSample32, (uint8_t *)outputPtr); - } - else - { - inputSample16 = *((uint16_t *)inputPtr); - framer.getAM824Sample(inputSample16, (uint8_t *)outputPtr); - } - outputPtr += 1; - inputPtr += userData->bytesPerSample; - bytesConverted += userData->bytesPerSample; - } - } - userData->totalBytes = (userData->totalBytes * sizeof(uint32_t))/userData->bytesPerSample; - userData->bytesPerSample = sizeof(uint32_t); - userData->bitsPerSample = 32; - userData->blockAlign = userData->numChannels * userData->bytesPerSample; + if (userData->am824_audio) { + framer.setAudioMode(); + } else { + framer.setDataMode(); + } + if (userData->am824_fs_match_wavfile) { + switch (userData->fs) { + case 32000: + framer.setSamplingFrequency(FS_32000_HZ); + break; + case 44100: + framer.setSamplingFrequency(FS_44100_HZ); + break; + case 48000: + framer.setSamplingFrequency(FS_48000_HZ); + break; + default: + framer.setSamplingFrequency(FS_NOT_INDICATED); + } + } else { + framer.setSamplingFrequency(userData->am824_fs); + } + if (userData->am824_professional) { + framer.setProfessionalMode(); + } else { + framer.setConsumerMode(); + } + outputMemSize = + (userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample; + *am824audio = malloc(outputMemSize); + outputPtr = (uint32_t*)*am824audio; + inputPtr = (uint8_t*)audio; + while (bytesConverted < userData->totalBytes) { + for (channel = 0; channel < userData->numChannels; channel++) { + if (userData->bytesPerSample == 3) { + inputSample32 = *((uint32_t*)inputPtr) & 0xffffff; + framer.getAM824Sample(inputSample32, (uint8_t*)outputPtr); + } else { + inputSample16 = *((uint16_t*)inputPtr); + framer.getAM824Sample(inputSample16, (uint8_t*)outputPtr); + } + outputPtr += 1; + inputPtr += userData->bytesPerSample; + bytesConverted += userData->bytesPerSample; + } + } + userData->totalBytes = + (userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample; + userData->bytesPerSample = sizeof(uint32_t); + userData->bitsPerSample = 32; + userData->blockAlign = userData->numChannels * userData->bytesPerSample; } -void list_devices(void) -{ - int i, numDevices, defaultDisplayed; - const PaDeviceInfo *deviceInfo; +void list_devices(void) { + int i, numDevices, defaultDisplayed; + const PaDeviceInfo* deviceInfo; - printf("PortAudio version: 0x%08X\n", Pa_GetVersion()); - printf("Version text: '%s'\n", Pa_GetVersionText()); + printf("PortAudio version: 0x%08X\n", Pa_GetVersion()); + printf("Version text: '%s'\n", Pa_GetVersionText()); - numDevices = Pa_GetDeviceCount(); - if (numDevices < 0) - { - throwError(ERR_NO_DEVICE, "Pa_GetDeviceCount returned 0x%x\n", numDevices); - } + numDevices = Pa_GetDeviceCount(); + if (numDevices < 0) { + throwError(ERR_NO_DEVICE, "Pa_GetDeviceCount returned 0x%x\n", numDevices); + } - printf("Number of devices = %d\n", numDevices); - for (i = 0; i < numDevices; i++) - { - deviceInfo = Pa_GetDeviceInfo(i); - if (deviceInfo->maxOutputChannels > 0) { - printf("--------------------------------------- device #%d\n", i); + printf("Number of devices = %d\n", numDevices); + for (i = 0; i < numDevices; i++) { + deviceInfo = Pa_GetDeviceInfo(i); + if (deviceInfo->maxOutputChannels > 0) { + printf("--------------------------------------- device #%d\n", i); - /* Mark global and API specific default devices */ - defaultDisplayed = 0; - if (i == Pa_GetDefaultOutputDevice()) - { - printf("[ Default Output"); - defaultDisplayed = 1; - } - else if (i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice) - { - const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); - printf("[ Default %s Output", hostInfo->name); - defaultDisplayed = 1; - } + /* Mark global and API specific default devices */ + defaultDisplayed = 0; + if (i == Pa_GetDefaultOutputDevice()) { + printf("[ Default Output"); + defaultDisplayed = 1; + } else if (i == + Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice) { + const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); + printf("[ Default %s Output", hostInfo->name); + defaultDisplayed = 1; + } - if (defaultDisplayed) - printf(" ]\n"); + if (defaultDisplayed) + printf(" ]\n"); - /* print device info fields */ + /* print device info fields */ #ifdef WIN32 - { /* Use wide char on windows, so we can show UTF-8 encoded device names */ - wchar_t wideName[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH - 1); - wprintf(L"Name = %s\n", wideName); - } + { /* Use wide char on windows, so we can show UTF-8 encoded device names + */ + wchar_t wideName[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, + MAX_PATH - 1); + wprintf(L"Name = %s\n", wideName); + } #else - printf("Name = %s\n", deviceInfo->name); + printf("Name = %s\n", deviceInfo->name); #endif - printf("Host API = %s\n", Pa_GetHostApiInfo(deviceInfo->hostApi)->name); - printf("Max output channels = %d\n", deviceInfo->maxOutputChannels); + printf("Host API = %s\n", + Pa_GetHostApiInfo(deviceInfo->hostApi)->name); + printf("Max output channels = %d\n", + deviceInfo->maxOutputChannels); - printf("Default low output latency = %4.4f\n", deviceInfo->defaultLowOutputLatency); - printf("Default high output latency = %4.4f\n", deviceInfo->defaultHighOutputLatency); + printf("Default low output latency = %4.4f\n", + deviceInfo->defaultLowOutputLatency); + printf("Default high output latency = %4.4f\n", + deviceInfo->defaultHighOutputLatency); #ifdef WIN32 #if PA_USE_ASIO - /* ASIO specific latency information */ - if (Pa_GetHostApiInfo(deviceInfo->hostApi)->type == paASIO) { - long minLatency, maxLatency, preferredLatency, granularity; + /* ASIO specific latency information */ + if (Pa_GetHostApiInfo(deviceInfo->hostApi)->type == paASIO) { + long minLatency, maxLatency, preferredLatency, granularity; - err = PaAsio_GetAvailableLatencyValues(i, - &minLatency, &maxLatency, &preferredLatency, &granularity); + err = PaAsio_GetAvailableLatencyValues(i, &minLatency, &maxLatency, + &preferredLatency, &granularity); - printf("ASIO minimum buffer size = %ld\n", minLatency); - printf("ASIO maximum buffer size = %ld\n", maxLatency); - printf("ASIO preferred buffer size = %ld\n", preferredLatency); + printf("ASIO minimum buffer size = %ld\n", minLatency); + printf("ASIO maximum buffer size = %ld\n", maxLatency); + printf("ASIO preferred buffer size = %ld\n", preferredLatency); - if (granularity == -1) - printf("ASIO buffer granularity = power of 2\n"); - else - printf("ASIO buffer granularity = %ld\n", granularity); - } + if (granularity == -1) + printf("ASIO buffer granularity = power of 2\n"); + else + printf("ASIO buffer granularity = %ld\n", granularity); + } #endif /* PA_USE_ASIO */ #endif /* WIN32 */ - printf("Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate); - } - } - return; + printf("Default sample rate = %8.2f\n", + deviceInfo->defaultSampleRate); + } + } + return; } -void print_usage(void) -{ - fprintf(stderr, "wavplay_am824 [OPTION]... v%s\n", VERSION_STRING); - fprintf(stderr, "Copyright Dolby Laboratories Inc., 2020. All rights reserved.\n\n"); - fprintf(stderr, "-h Display this messgage\n"); - fprintf(stderr, "-ld List playback devices\n"); - fprintf(stderr, "-l Preferred playout latency in seconds\n"); - fprintf(stderr, "-buf Playout buffer size in frames (samples x channels)\n"); - fprintf(stderr, "-d Device index to use for playback\n"); - fprintf(stderr, "-am824 Using virtual sound card feeding an AM824/2110-31 stream\n"); - fprintf(stderr, " The following keywords can follow the '-am824' switch to modify channel status:\n"); - fprintf(stderr, " audio, nonaudio, fs_not_indicated, fs_48k, fs_441k, fs_32k, professional, consumer\n"); +void print_usage(void) { + fprintf(stderr, "wavplay_am824 [OPTION]... v%s\n", + VERSION_STRING); + fprintf(stderr, + "Copyright Dolby Laboratories Inc., 2020. All rights reserved.\n\n"); + fprintf(stderr, "-h Display this messgage\n"); + fprintf(stderr, "-ld List playback devices\n"); + fprintf(stderr, + "-l Preferred playout latency in seconds\n"); + fprintf(stderr, + "-buf Playout buffer size in frames (samples x " + "channels)\n"); + fprintf(stderr, "-d Device index to use for playback\n"); + fprintf(stderr, + "-am824 Using virtual sound card feeding an " + "AM824/2110-31 stream\n"); + fprintf(stderr, + " The following keywords can follow the '-am824' " + "switch to modify channel status:\n"); + fprintf(stderr, + " audio, nonaudio, fs_not_indicated, fs_48k, fs_441k, fs_32k, " + "professional, consumer\n"); #if defined(_WIN32) || defined(_WIN64) - fprintf(stderr, "-e Uses WASPI exclusive mode if selected device is a WASAPI device\n"); + fprintf(stderr, + "-e Uses WASPI exclusive mode if selected device " + "is a WASAPI device\n"); #endif - fprintf(stderr, "\n"); + fprintf(stderr, "\n"); } - /*******************************************************************/ -int main(int argc, char *argv[]) -{ - PaStreamParameters outputParameters; - PaStream* stream; - PaError err = paNoError; - UserData data; - unsigned int i; - unsigned long bytesRead; - unsigned int framesPerBuffer = 128; - float userLatency = 0.0; - char playbackWavFileName[256] = ""; - unsigned int bitDepth = 16; - PaSampleFormat sampleFormat = paInt16; - double sampleRate = 48000.0; - double startTime, finishTime; - void *audio; +int main(int argc, char* argv[]) { + PaStreamParameters outputParameters; + PaStream* stream; + PaError err = paNoError; + UserData data; + int i; + unsigned long bytesRead; + unsigned int framesPerBuffer = 128; + float userLatency = 0.0; + char playbackWavFileName[256] = ""; + double sampleRate = 48000.0; + double startTime/*,finishTime*/; + void* audio; #if defined(_WIN32) || defined(_WIN64) - struct PaWasapiStreamInfo wasapiInfo; - int waspiExclusiveMode = 0; + struct PaWasapiStreamInfo wasapiInfo; + int waspiExclusiveMode = 0; #endif - unsigned int am824Mode = 0; - void *am824audio; + unsigned int am824Mode = 0; + void* am824audio; + outputParameters.device = paNoDevice; + audio = NULL; - outputParameters.device = paNoDevice; - audio = NULL; + err = Pa_Initialize(); - err = Pa_Initialize(); + if (err != paNoError) { + throwError(ERR_PORTAUDIO, "Pa_Initialize returned %d, %s", err, + Pa_GetErrorText(err)); + } - if( err != paNoError ) - { - throwError(ERR_PORTAUDIO, "Pa_Initialize returned %d, %s", err, Pa_GetErrorText(err)); - } - - if (argc < 2) - { - print_usage(); - list_devices(); - exit(0); - } - - for (i = 1; i < (unsigned int)argc; i++) - { - // check to see if its a filename first - if ((argv[i][0] != '-') && (strlen(playbackWavFileName) == 0)) - { - strcpy(playbackWavFileName, argv[i]); - } - else if (!strcmp(argv[i], "-d")) - { - if (i == (argc - 1)) - { - print_usage(); - throwError(ERR_NO_DEVICE, "Can't find soundcard index"); - } - outputParameters.device = atoi(argv[i + 1]); - // We increment i here to step over the next parameter - // which has been parsed as the value - i++; - } -#if defined(_WIN32) || defined(_WIN64) - else if (!strcmp(argv[i], "-e")) - { - waspiExclusiveMode = 1; - } -#endif - else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) - { - print_usage(); - exit(0); - } - else if (!strcmp(argv[i], "-buf")) - { - if (i == (argc - 1)) - { - print_usage(); - throwError(ERR_NO_DEVICE, "Can't find buffer size"); - } - framesPerBuffer = atoi(argv[i + 1]); - // We increment i here to step over the next parameter - // which has been parsed as the value - i++; - } - else if (!strcmp(argv[i], "-l")) - { - if (i == (argc - 1)) - { - print_usage(); - throwError(ERR_NO_DEVICE, "Can't find latency"); - } - userLatency = atof(argv[i + 1]); - // We increment i here to step over the next parameter - // which has been parsed as the value - i++; - } - else if (!strcmp(argv[i], "-ld")) - { - list_devices(); - exit(0); - } - else if (!strcmp(argv[i], "-am824")) - { - am824Mode = 1; - data.am824_audio = 1; - data.am824_professional = 0; - data.am824_fs_match_wavfile = 1; - i++; - while((i < (unsigned int)argc) && (argv[i][0] != '-')) - { - if (!strcmp(argv[i], "audio")) - { - data.am824_audio=1; - } - else if (!strcmp(argv[i], "nonaudio")) - { - data.am824_audio=0; - } - else if (!strcmp(argv[i], "fs_not_indicated")) - { - data.am824_fs = FS_NOT_INDICATED; - data.am824_fs_match_wavfile = 0; - } - else if (!strcmp(argv[i], "fs_48k")) - { - data.am824_fs = FS_48000_HZ; - data.am824_fs_match_wavfile = 0; - - } - else if (!strcmp(argv[i], "fs_441k")) - { - data.am824_fs = FS_44100_HZ; - data.am824_fs_match_wavfile = 0; - } - else if (!strcmp(argv[i], "fs_32k")) - { - data.am824_fs = FS_32000_HZ; - data.am824_fs_match_wavfile = 0; - } - else if (!strcmp(argv[i], "professional")) - { - data.am824_professional=1; - } - else if (!strcmp(argv[i], "consumer")) - { - data.am824_professional=0; - } - else - { - break; - } - i++; - } - i--; - } - else - { - print_usage(); - throwError(ERR_BAD_CMD_OPTION, "Option %s not recognized", argv[i]); - } - } - - if (read_wav_file_header(playbackWavFileName, &data)) - { - throwError(ERR_BAD_WAV_FORMAT, "Bad wav header"); - } - - audio = malloc(data.totalBytes); - if (!audio) - { - throwError(ERR_NO_MEMORY, "Memory allocation failed"); - } - - bytesRead = read_entire_wav_file(&data, audio); - if (bytesRead != data.totalBytes) - { - throwError(ERR_INCOMPLETE_INPUT_FILE, "Couldn't read all input data"); - } - - if (am824Mode) - { - printf("AM824 output mode selected\n"); - am824Convert(&data, audio, &am824audio); - free(audio); - audio = am824audio; - } - - if (outputParameters.device == paNoDevice) - { - outputParameters.device = Pa_GetDefaultOutputDevice(); - } - - if (outputParameters.device == paNoDevice) - { - throwError(ERR_NO_DEVICE, "No default output device"); - } - -#if defined(_WIN32) || defined(_WIN64) - if ((waspiExclusiveMode) && - (Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->type == paWASAPI)) { - wasapiInfo.size = sizeof(PaWasapiStreamInfo); - wasapiInfo.hostApiType = paWASAPI; - wasapiInfo.version = 1; - wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority); - wasapiInfo.channelMask = 0; - wasapiInfo.hostProcessorOutput = NULL; - wasapiInfo.hostProcessorInput = NULL; - wasapiInfo.threadPriority = eThreadPriorityProAudio; - outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo); - printf("Detected WASAPI device and setting exclusive mode\n"); - } - else - { -#endif - outputParameters.hostApiSpecificStreamInfo = NULL; -#if defined(_WIN32) || defined(_WIN64) - } -#endif - - if (userLatency > 0.0) - { - outputParameters.suggestedLatency = userLatency; - } - else - { - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - } - - switch(data.waveFileFormat) - { - case FF_PCM: - - if ((data.bitsPerSample != 8) && (data.bitsPerSample != 16) - && (data.bitsPerSample != 24) && (data.bitsPerSample != 32)) - { - throwError(ERR_NOT_SUPPORTED, "Unsupported WAVE_FORMAT_PCM bitdepth"); - } - break; - case FF_FLOAT32: - if (data.bitsPerSample != 32) - { - throwError(ERR_BAD_WAV_FORMAT, "Wavefile indicated floating point but bits per sample is not 32"); - } - break; - default: - throwError(ERR_NOT_SUPPORTED, "Unsupported WAV format tag (WAVE_FORMAT_PCM & WAVE_FORMAT_IEEE_FLOAT supported)"); - } - - if (data.bitsPerSample == 16) - { - outputParameters.sampleFormat = paInt16; - } - else if (data.bitsPerSample == 8) - { - outputParameters.sampleFormat = paInt8; - } - else if (data.bitsPerSample == 24) - { - outputParameters.sampleFormat = paInt24; - } - else if (data.bitsPerSample == 32) - { - if (data.waveFileFormat == FF_FLOAT32) - { - outputParameters.sampleFormat = paFloat32; - } - else - { - outputParameters.sampleFormat = paInt32; - } - } - else - { - throwError(ERR_NOT_SUPPORTED, "Unsupported bitdepth %d", data.bitsPerSample); - } - - outputParameters.channelCount = data.numChannels; - sampleRate = (double)data.fs; - err = Pa_IsFormatSupported(NULL, &outputParameters, sampleRate); - if (err != paNoError) - { - throwError(ERR_NOT_SUPPORTED, "Pa_IsFormatSupported returned %d, %s", err, Pa_GetErrorText(err)); - } - - printf("Pa_IsFormatSupported succeeded\n"); - - printf("device: %u\nchannels: %u\nsampleFormat: %lu\nlatency: %f\nsampleRate: %u\n", outputParameters.device, outputParameters.channelCount, outputParameters.sampleFormat, outputParameters.suggestedLatency, data.fs); - - // Set callback parameters - data.frameIndex = 0; /* Index into sample array. */ - data.maxFrameIndex = (unsigned long) data.totalBytes / data.blockAlign; - data.audio = (unsigned char *)audio; - - printf("\n=== Now playing back. ===\n"); fflush(stdout); - err = Pa_OpenStream( - &stream, - NULL, /* no input */ - &outputParameters, - data.fs, - framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - playCallback, - &data ); - if( err != paNoError ) - { - Pa_Terminate(); - throwError(ERR_NO_STREAM, "Pa_OpenStream returned %d, %s", err, Pa_GetErrorText( err )); - } - - if( stream ) - { -#if defined(__linux__ ) - PaAlsa_EnableRealtimeScheduling( stream, 1 ); - printf("RealTime Scheduling enabled\n"); -#endif - err = Pa_StartStream( stream ); - if( err != paNoError ) - { - Pa_Terminate(); - throwError(ERR_NO_STREAM, "Pa_StartStream returned %d, %s", err, Pa_GetErrorText( err )); - } - printf("Waiting for playback to start.\n"); - - do - { - startTime = Pa_GetStreamTime(stream); - } - while (startTime == 0.0); - - printf("Waiting for playback to finish.\n"); - fflush(stdout); - finishTime = data.maxFrameIndex / data.fs; - while ((err = Pa_IsStreamActive(stream)) == 1) - { - Pa_Sleep(1000); - } - printf("\n"); - - if( err != paNoError ) - { - Pa_Terminate(); - throwError(ERR_NO_STREAM, "Pa error %d received during playback: %s", err, Pa_GetErrorText( err )); - } - - err = Pa_CloseStream( stream ); - if( err != paNoError ) - - - printf("Done.\n"); fflush(stdout); - } - - if (audio) - { - free(audio); - } - Pa_Terminate(); + if (argc < 2) { + print_usage(); + list_devices(); exit(0); -} + } + for (i = 1; i < argc; i++) { + // check to see if its a filename first + if ((argv[i][0] != '-') && (strlen(playbackWavFileName) == 0)) { + strcpy(playbackWavFileName, argv[i]); + } else if (!strcmp(argv[i], "-d")) { + if (i == (argc - 1)) { + print_usage(); + throwError(ERR_NO_DEVICE, "Can't find soundcard index"); + } + outputParameters.device = atoi(argv[i + 1]); + // We increment i here to step over the next parameter + // which has been parsed as the value + i++; + } +#if defined(_WIN32) || defined(_WIN64) + else if (!strcmp(argv[i], "-e")) { + waspiExclusiveMode = 1; + } +#endif + else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || + !strcmp(argv[i], "--help")) { + print_usage(); + exit(0); + } else if (!strcmp(argv[i], "-buf")) { + if (i == (argc - 1)) { + print_usage(); + throwError(ERR_NO_DEVICE, "Can't find buffer size"); + } + framesPerBuffer = atoi(argv[i + 1]); + // We increment i here to step over the next parameter + // which has been parsed as the value + i++; + } else if (!strcmp(argv[i], "-l")) { + if (i == (argc - 1)) { + print_usage(); + throwError(ERR_NO_DEVICE, "Can't find latency"); + } + userLatency = atof(argv[i + 1]); + // We increment i here to step over the next parameter + // which has been parsed as the value + i++; + } else if (!strcmp(argv[i], "-ld")) { + list_devices(); + exit(0); + } else if (!strcmp(argv[i], "-am824")) { + am824Mode = 1; + data.am824_audio = 1; + data.am824_professional = 0; + data.am824_fs_match_wavfile = 1; + i++; + while ((i < argc) && (argv[i][0] != '-')) { + if (!strcmp(argv[i], "audio")) { + data.am824_audio = 1; + } else if (!strcmp(argv[i], "nonaudio")) { + data.am824_audio = 0; + } else if (!strcmp(argv[i], "fs_not_indicated")) { + data.am824_fs = FS_NOT_INDICATED; + data.am824_fs_match_wavfile = 0; + } else if (!strcmp(argv[i], "fs_48k")) { + data.am824_fs = FS_48000_HZ; + data.am824_fs_match_wavfile = 0; + + } else if (!strcmp(argv[i], "fs_441k")) { + data.am824_fs = FS_44100_HZ; + data.am824_fs_match_wavfile = 0; + } else if (!strcmp(argv[i], "fs_32k")) { + data.am824_fs = FS_32000_HZ; + data.am824_fs_match_wavfile = 0; + } else if (!strcmp(argv[i], "professional")) { + data.am824_professional = 1; + } else if (!strcmp(argv[i], "consumer")) { + data.am824_professional = 0; + } else { + break; + } + i++; + } + i--; + } else { + print_usage(); + throwError(ERR_BAD_CMD_OPTION, "Option %s not recognized", argv[i]); + } + } + + if (read_wav_file_header(playbackWavFileName, &data)) { + throwError(ERR_BAD_WAV_FORMAT, "Bad wav header"); + } + + audio = malloc(data.totalBytes); + if (!audio) { + throwError(ERR_NO_MEMORY, "Memory allocation failed"); + } + + bytesRead = read_entire_wav_file(&data, audio); + if (bytesRead != data.totalBytes) { + throwError(ERR_INCOMPLETE_INPUT_FILE, "Couldn't read all input data"); + } + + if (am824Mode) { + printf("AM824 output mode selected\n"); + am824Convert(&data, audio, &am824audio); + free(audio); + audio = am824audio; + } + + if (outputParameters.device == paNoDevice) { + outputParameters.device = Pa_GetDefaultOutputDevice(); + } + + if (outputParameters.device == paNoDevice) { + throwError(ERR_NO_DEVICE, "No default output device"); + } + +#if defined(_WIN32) || defined(_WIN64) + if ((waspiExclusiveMode) && + (Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi) + ->type == paWASAPI)) { + wasapiInfo.size = sizeof(PaWasapiStreamInfo); + wasapiInfo.hostApiType = paWASAPI; + wasapiInfo.version = 1; + wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority); + wasapiInfo.channelMask = 0; + wasapiInfo.hostProcessorOutput = NULL; + wasapiInfo.hostProcessorInput = NULL; + wasapiInfo.threadPriority = eThreadPriorityProAudio; + outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo); + printf("Detected WASAPI device and setting exclusive mode\n"); + } else { +#endif + outputParameters.hostApiSpecificStreamInfo = NULL; +#if defined(_WIN32) || defined(_WIN64) + } +#endif + + if (userLatency > 0.0) { + outputParameters.suggestedLatency = userLatency; + } else { + outputParameters.suggestedLatency = + Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; + } + + switch (data.waveFileFormat) { + case FF_PCM: + + if ((data.bitsPerSample != 8) && (data.bitsPerSample != 16) && + (data.bitsPerSample != 24) && (data.bitsPerSample != 32)) { + throwError(ERR_NOT_SUPPORTED, "Unsupported WAVE_FORMAT_PCM bitdepth"); + } + break; + case FF_FLOAT32: + if (data.bitsPerSample != 32) { + throwError( + ERR_BAD_WAV_FORMAT, + "Wavefile indicated floating point but bits per sample is not 32"); + } + break; + default: + throwError(ERR_NOT_SUPPORTED, + "Unsupported WAV format tag (WAVE_FORMAT_PCM & " + "WAVE_FORMAT_IEEE_FLOAT supported)"); + } + + if (data.bitsPerSample == 16) { + outputParameters.sampleFormat = paInt16; + } else if (data.bitsPerSample == 8) { + outputParameters.sampleFormat = paInt8; + } else if (data.bitsPerSample == 24) { + outputParameters.sampleFormat = paInt24; + } else if (data.bitsPerSample == 32) { + if (data.waveFileFormat == FF_FLOAT32) { + outputParameters.sampleFormat = paFloat32; + } else { + outputParameters.sampleFormat = paInt32; + } + } else { + throwError(ERR_NOT_SUPPORTED, "Unsupported bitdepth %d", + data.bitsPerSample); + } + + outputParameters.channelCount = data.numChannels; + sampleRate = (double)data.fs; + err = Pa_IsFormatSupported(NULL, &outputParameters, sampleRate); + if (err != paNoError) { + throwError(ERR_NOT_SUPPORTED, "Pa_IsFormatSupported returned %d, %s", err, + Pa_GetErrorText(err)); + } + + printf("Pa_IsFormatSupported succeeded\n"); + + printf( + "device: %u\nchannels: %u\nsampleFormat: %lu\nlatency: %f\nsampleRate: " + "%u\n", + outputParameters.device, outputParameters.channelCount, + outputParameters.sampleFormat, outputParameters.suggestedLatency, + data.fs); + + // Set callback parameters + data.frameIndex = 0; /* Index into sample array. */ + data.maxFrameIndex = (unsigned long)data.totalBytes / data.blockAlign; + data.audio = (unsigned char*)audio; + + printf("\n=== Now playing back. ===\n"); + fflush(stdout); + err = Pa_OpenStream(&stream, NULL, /* no input */ + &outputParameters, data.fs, framesPerBuffer, + paClipOff, /* we won't output out of range samples so + don't bother clipping them */ + playCallback, &data); + if (err != paNoError) { + Pa_Terminate(); + throwError(ERR_NO_STREAM, "Pa_OpenStream returned %d, %s", err, + Pa_GetErrorText(err)); + } + + if (stream) { +#if defined(__linux__) + PaAlsa_EnableRealtimeScheduling(stream, 1); + printf("RealTime Scheduling enabled\n"); +#endif + err = Pa_StartStream(stream); + if (err != paNoError) { + Pa_Terminate(); + throwError(ERR_NO_STREAM, "Pa_StartStream returned %d, %s", err, + Pa_GetErrorText(err)); + } + printf("Waiting for playback to start.\n"); + + do { + startTime = Pa_GetStreamTime(stream); + } while (startTime == 0.0); + + printf("Waiting for playback to finish.\n"); + fflush(stdout); + //finishTime = data.maxFrameIndex / data.fs; + while ((err = Pa_IsStreamActive(stream)) == 1) { + Pa_Sleep(1000); + } + printf("\n"); + + if (err != paNoError) { + Pa_Terminate(); + throwError(ERR_NO_STREAM, "Pa error %d received during playback: %s", err, + Pa_GetErrorText(err)); + } + + err = Pa_CloseStream(stream); + if (err != paNoError) + + printf("Done.\n"); + fflush(stdout); + } + + if (audio) { + free(audio); + } + Pa_Terminate(); + exit(0); +}