From 344fba082787ae37cc75c54ac57f12ebaf740481 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Sun, 30 Aug 2020 16:16:25 +0200 Subject: [PATCH] Minor changes to AM824 WAV player before merge into master branch --- wavplay_am824/.clang-format | 157 +++ wavplay_am824/CMakeLists.txt | 8 + wavplay_am824/README.md | 12 + wavplay_am824/am824_framer.h | 471 ++++---- wavplay_am824/bin/Linux/wavplay_am824 | Bin 44304 -> 0 bytes wavplay_am824/ubuntu-packages.sh | 9 + wavplay_am824/wavplay_am824.cpp | 1428 ++++++++++++------------- 7 files changed, 1075 insertions(+), 1010 deletions(-) create mode 100644 wavplay_am824/.clang-format create mode 100644 wavplay_am824/CMakeLists.txt create mode 100644 wavplay_am824/README.md delete mode 100644 wavplay_am824/bin/Linux/wavplay_am824 create mode 100755 wavplay_am824/ubuntu-packages.sh 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 c6d935a4f7f358e8817adc9ecca238c383ade341..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44304 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 #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); +}