Minor changes to AM824 WAV player before merge into master branch

This commit is contained in:
Andrea Bondavalli 2020-08-30 16:16:25 +02:00
parent d7a117e482
commit 344fba0827
7 changed files with 1075 additions and 1010 deletions

157
wavplay_am824/.clang-format Normal file
View File

@ -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: '^<ext/.*\.h>'
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
...

View File

@ -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})

12
wavplay_am824/README.md Normal file
View File

@ -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

View File

@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/> * along with this program. If not, see <http://www.gnu.org/licenses/>
************************************************************************************************************/ ************************************************************************************************************/
#include <stdint.h> #include <stdint.h>
#define CHANNEL_STATUS_BYTES 24 #define CHANNEL_STATUS_BYTES 24
@ -24,14 +23,22 @@
#define BOTTOMBIT 1 #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 }; enum AM824Endianess { AM824_BIG_ENDIAN, AM824_LITTLE_ENDIAN };
class AM824Framer class AM824Framer {
{
uint8_t channelStatusIndex; uint8_t channelStatusIndex;
uint8_t channelStatusMask; uint8_t channelStatusMask;
uint8_t channelStatus[CHANNEL_STATUS_BYTES]; uint8_t channelStatus[CHANNEL_STATUS_BYTES];
@ -41,61 +48,52 @@ class AM824Framer
uint8_t crcTable[256]; uint8_t crcTable[256];
AM824Endianess endian; AM824Endianess endian;
static uint8_t getParity(unsigned int n) static uint8_t getParity(unsigned int n) {
{
uint8_t parity = 0; uint8_t parity = 0;
while (n) while (n) {
{
parity = 1 - parity; parity = 1 - parity;
n = n & (n - 1); n = n & (n - 1);
} }
return parity; return parity;
} }
void crcTableInit(void) void crcTableInit(void) {
{
uint8_t remainder; uint8_t remainder;
for (int dividend = 0; dividend < 256; ++dividend) for (int dividend = 0; dividend < 256; ++dividend) {
{
remainder = dividend << (WIDTH - 8); remainder = dividend << (WIDTH - 8);
for (uint8_t bit = 0; bit < 8; bit++) for (uint8_t bit = 0; bit < 8; bit++) {
{ if (remainder & BOTTOMBIT) {
if (remainder & BOTTOMBIT)
{
remainder = (remainder >> 1) ^ REFLECTED_POLYNOMIAL; remainder = (remainder >> 1) ^ REFLECTED_POLYNOMIAL;
} } else {
else
{
remainder = (remainder >> 1); remainder = (remainder >> 1);
} }
} }
crcTable[dividend] = remainder; crcTable[dividend] = remainder;
} }
} }
void setCRC(void) void setCRC(void) {
{
uint8_t data; uint8_t data;
uint8_t remainder = 0xff; uint8_t remainder = 0xff;
for (int byte = 0; byte < 23; byte++) for (int byte = 0; byte < 23; byte++) {
{
data = channelStatus[byte] ^ remainder; data = channelStatus[byte] ^ remainder;
remainder = crcTable[data]; remainder = crcTable[data];
} }
channelStatus[23] = remainder; channelStatus[23] = remainder;
} }
public: public:
// Input number of channels and the bitdepth of the input samples // Input number of channels and the bitdepth of the input samples
// Note that the output bit depth is always 24 bit // Note that the output bit depth is always 24 bit
AM824Framer(uint8_t newNumChannels, /* Input - Number of channels of input/output audio */ AM824Framer(
uint8_t newBitDepth, /* Input - Bit depth of input audio, output is always 32 bit */ uint8_t
AM824Endianess outputEndianess, /* Input = Endianess of output samples, input is always machine order */ 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 */ AM824ErrorCode& err) /* Output - error code */
{ {
uint8_t i; uint8_t i;
@ -108,8 +106,7 @@ public:
endian = outputEndianess; endian = outputEndianess;
// Set certain channel status bits // Set certain channel status bits
// Clear it first // Clear it first
for (i = 0 ; i < CHANNEL_STATUS_BYTES ; i++) for (i = 0; i < CHANNEL_STATUS_BYTES; i++) {
{
channelStatus[i] = 0; channelStatus[i] = 0;
} }
// Default Channel Status // Default Channel Status
@ -120,8 +117,7 @@ public:
// 48kHz // 48kHz
channelStatus[0] |= 2 << 6; channelStatus[0] |= 2 << 6;
switch(bitDepth) switch (bitDepth) {
{
case 16: case 16:
channelStatus[2] |= 1 << 3; channelStatus[2] |= 1 << 3;
break; break;
@ -146,10 +142,8 @@ public:
err = AM824_ERR_OK; err = AM824_ERR_OK;
} }
AM824ErrorCode setSamplingFrequency(AM824SamplingFrequency fs_code) AM824ErrorCode setSamplingFrequency(AM824SamplingFrequency fs_code) {
{ if (fs_code > FS_32000_HZ) {
if (fs_code > FS_32000_HZ)
{
return (AM824_ERR_BAD_SAMPLING_FREQUENCY); return (AM824_ERR_BAD_SAMPLING_FREQUENCY);
} }
// Reset top two bits and set accordingly // Reset top two bits and set accordingly
@ -159,55 +153,46 @@ public:
return (AM824_ERR_OK); return (AM824_ERR_OK);
} }
void setProfessionalMode(void) void setProfessionalMode(void) {
{
channelStatus[0] |= 1; channelStatus[0] |= 1;
setCRC(); setCRC();
} }
void setConsumerMode(void) void setConsumerMode(void) {
{
channelStatus[0] &= 0xfe; channelStatus[0] &= 0xfe;
setCRC(); setCRC();
} }
void setAudioMode(void) void setAudioMode(void) {
{
channelStatus[0] &= 0xfd; channelStatus[0] &= 0xfd;
setCRC(); setCRC();
} }
void setDataMode(void) void setDataMode(void) {
{
channelStatus[0] |= 2; channelStatus[0] |= 2;
setCRC(); setCRC();
} }
void getAM824Sample(uint32_t inputSample, uint8_t *outputBytes) void getAM824Sample(uint32_t inputSample, uint8_t* outputBytes) {
{
uint32_t outputSample; uint32_t outputSample;
bool channelStatusBit = channelStatus[channelStatusIndex] & channelStatusMask; bool channelStatusBit =
channelStatus[channelStatusIndex] & channelStatusMask;
bool userBit = false; bool userBit = false;
bool validityBit = false; bool validityBit = false;
// Input samples are MSB justified as per AES3 // Input samples are MSB justified as per AES3
if (bitDepth == 16) if (bitDepth == 16) {
{
outputSample = inputSample << 8; outputSample = inputSample << 8;
} } else {
else
{
outputSample = inputSample; outputSample = inputSample;
} }
// Detect block start // Detect block start
if ((channelStatusIndex == 0) && (channelStatusMask == 1)) if ((channelStatusIndex == 0) && (channelStatusMask == 1)) {
{
outputSample |= 1 << 29; outputSample |= 1 << 29;
} }
// Detect frame start // Detect frame start
if (subFrameCounter == 0) if (subFrameCounter == 0) {
{
outputSample |= 1 << 28; outputSample |= 1 << 28;
} }
@ -219,37 +204,30 @@ public:
outputSample |= getParity(outputSample) << 27; outputSample |= getParity(outputSample) << 27;
// Now complete all the wraparound checks // Now complete all the wraparound checks
// Note that channel status chan be different for the different subframes (channels) // Note that channel status chan be different for the different subframes
// but in this example channel status is set to be the same for all subframes (channels) // (channels) but in this example channel status is set to be the same for
// all subframes (channels)
subFrameCounter++; subFrameCounter++;
if (subFrameCounter == numChannels) if (subFrameCounter == numChannels) {
{
subFrameCounter = 0; subFrameCounter = 0;
// Move to next channel status bit // Move to next channel status bit
if (channelStatusMask == 0x80) if (channelStatusMask == 0x80) {
{
channelStatusMask = 1; channelStatusMask = 1;
channelStatusIndex++; channelStatusIndex++;
if (channelStatusIndex == CHANNEL_STATUS_BYTES) if (channelStatusIndex == CHANNEL_STATUS_BYTES) {
{
channelStatusIndex = 0; channelStatusIndex = 0;
} }
} } else {
else
{
channelStatusMask = channelStatusMask << 1; channelStatusMask = channelStatusMask << 1;
} }
} }
if (endian == AM824_BIG_ENDIAN) if (endian == AM824_BIG_ENDIAN) {
{
// Return in 32 bit Big Endian format // Return in 32 bit Big Endian format
*outputBytes++ = (outputSample & 0xff000000) >> 24; *outputBytes++ = (outputSample & 0xff000000) >> 24;
*outputBytes++ = (outputSample & 0x00ff0000) >> 16; *outputBytes++ = (outputSample & 0x00ff0000) >> 16;
*outputBytes++ = (outputSample & 0x0000ff00) >> 8; *outputBytes++ = (outputSample & 0x0000ff00) >> 8;
*outputBytes++ = (outputSample & 0x000000ff) >> 0; *outputBytes++ = (outputSample & 0x000000ff) >> 0;
} } else {
else
{
// Return in 32 bit Little Endian format // Return in 32 bit Little Endian format
*outputBytes++ = (outputSample & 0x000000ff) >> 0; *outputBytes++ = (outputSample & 0x000000ff) >> 0;
*outputBytes++ = (outputSample & 0x0000ff00) >> 8; *outputBytes++ = (outputSample & 0x0000ff00) >> 8;
@ -260,11 +238,9 @@ public:
/* Simple test code to check CRC implementation */ /* Simple test code to check CRC implementation */
/* See EBU Tech 3250 or AES3 for the reference for these examples */ /* See EBU Tech 3250 or AES3 for the reference for these examples */
void testCRC(void) void testCRC(void) {
{
unsigned int i; unsigned int i;
for (i = 0 ; i < CHANNEL_STATUS_BYTES ; i++) for (i = 0; i < CHANNEL_STATUS_BYTES; i++) {
{
channelStatus[i] = 0; channelStatus[i] = 0;
} }
// From AES3-2-2009-r2019 - Example 1 // From AES3-2-2009-r2019 - Example 1
@ -272,26 +248,21 @@ public:
channelStatus[1] = 2; channelStatus[1] = 2;
channelStatus[4] = 2; channelStatus[4] = 2;
setCRC(); setCRC();
if (channelStatus[23] == 0x9b) if (channelStatus[23] == 0x9b) {
{
printf("Example 1 - passed\n"); printf("Example 1 - passed\n");
} } else {
else printf("Example 1 - failed, expecting 0x9b, got 0x%x\n",
{ channelStatus[23]);
printf("Example 1 - failed, expecting 0x9b, got 0x%x\n",channelStatus[23]);
} }
channelStatus[0] = 0x01; channelStatus[0] = 0x01;
channelStatus[1] = 0; channelStatus[1] = 0;
channelStatus[4] = 0; channelStatus[4] = 0;
setCRC(); setCRC();
if (channelStatus[23] == 0x32) if (channelStatus[23] == 0x32) {
{
printf("Example 2 - passed\n"); printf("Example 2 - passed\n");
} } else {
else printf("Example 2 - failed, expecting 0x32, got 0x%x\n",
{ channelStatus[23]);
printf("Example 2 - failed, expecting 0x32, got 0x%x\n",channelStatus[23]);
} }
} }
}; };

View File

@ -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

View File

@ -43,16 +43,12 @@
#include <sndfile.h> #include <sndfile.h>
#endif #endif
typedef enum typedef enum {
{
FF_PCM = 0, FF_PCM = 0,
FF_FLOAT32, FF_FLOAT32,
} } FileFormat;
FileFormat;
typedef enum {
typedef enum
{
ERR_FILE_NOT_FOUND = -101, ERR_FILE_NOT_FOUND = -101,
ERR_BAD_INPUT_FILE, ERR_BAD_INPUT_FILE,
ERR_BAD_WAV_FORMAT, ERR_BAD_WAV_FORMAT,
@ -64,12 +60,9 @@ typedef enum
ERR_NO_STREAM, ERR_NO_STREAM,
ERR_PORTAUDIO, ERR_PORTAUDIO,
ERR_OK = 0 ERR_OK = 0
} } ErrorCode;
ErrorCode;
typedef struct {
typedef struct
{
unsigned long frameIndex; /* Index into sample array. */ unsigned long frameIndex; /* Index into sample array. */
unsigned long maxFrameIndex; unsigned long maxFrameIndex;
unsigned int numChannels; unsigned int numChannels;
@ -89,11 +82,9 @@ typedef struct
AM824SamplingFrequency am824_fs; AM824SamplingFrequency am824_fs;
unsigned int am824_fs_match_wavfile; unsigned int am824_fs_match_wavfile;
unsigned int am824_professional; unsigned int am824_professional;
} } UserData;
UserData;
void throwError(ErrorCode err, const char *format, ...) void throwError(ErrorCode err, const char* format, ...) {
{
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@ -105,16 +96,18 @@ void throwError(ErrorCode err, const char *format, ...)
exit(err); 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, static int playCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void *userData ) void* userData) {
{
UserData* data = (UserData*)userData; UserData* data = (UserData*)userData;
unsigned char *rptr = &data->audio[data->frameIndex * data->numChannels * data->bytesPerSample]; unsigned char* rptr =
&data->audio[data->frameIndex * data->numChannels * data->bytesPerSample];
unsigned char* wptr = (unsigned char*)outputBuffer; unsigned char* wptr = (unsigned char*)outputBuffer;
unsigned int i; unsigned int i;
int finished; int finished;
@ -125,24 +118,21 @@ static int playCallback( const void *inputBuffer, void *outputBuffer,
(void)statusFlags; (void)statusFlags;
(void)userData; (void)userData;
if( framesLeft < framesPerBuffer ) if (framesLeft < framesPerBuffer) {
{
/* final buffer... */ /* final buffer... */
for( i=0; i<framesLeft * data->numChannels * data->bytesPerSample; i++) for (i = 0; i < framesLeft * data->numChannels * data->bytesPerSample;
{ i++) {
*wptr++ = *rptr++; *wptr++ = *rptr++;
} }
for( ; i<framesPerBuffer * data->numChannels * data->bytesPerSample; i++ ) for (; i < framesPerBuffer * data->numChannels * data->bytesPerSample;
{ i++) {
*wptr++ = 0; *wptr++ = 0;
} }
data->frameIndex += framesLeft; data->frameIndex += framesLeft;
finished = paComplete; finished = paComplete;
} } else {
else for (i = 0; i < framesPerBuffer * data->numChannels * data->bytesPerSample;
{ i++) {
for( i=0; i< framesPerBuffer * data->numChannels * data->bytesPerSample; i++)
{
*wptr++ = *rptr++; *wptr++ = *rptr++;
} }
data->frameIndex += framesPerBuffer; data->frameIndex += framesPerBuffer;
@ -151,8 +141,9 @@ static int playCallback( const void *inputBuffer, void *outputBuffer,
return finished; return finished;
} }
/* Two versions of the following two functions exist, one for Windows and one for Linux. This approach was chosen /* Two versions of the following two functions exist, one for Windows and one
because a platform independent wav file library was not used but rather Windows API and libasound directly */ 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) #if defined(_WIN32) || defined(_WIN64)
int read_wav_file_header(char* playbackWaveFile, /* Input file name string */ int read_wav_file_header(char* playbackWaveFile, /* Input file name string */
@ -162,40 +153,39 @@ int read_wav_file_header(char *playbackWaveFile, /* Input file name string */
MMCKINFO mmckinfoSubchunk; MMCKINFO mmckinfoSubchunk;
WAVEFORMATEXTENSIBLE* format; WAVEFORMATEXTENSIBLE* format;
outputData->waveFile = mmioOpenA(playbackWaveFile, 0, MMIO_READ | MMIO_ALLOCBUF); outputData->waveFile =
if (!outputData->waveFile) mmioOpenA(playbackWaveFile, 0, MMIO_READ | MMIO_ALLOCBUF);
{ if (!outputData->waveFile) {
throwError(ERR_FILE_NOT_FOUND, "Can't Open %s!", playbackWaveFile); throwError(ERR_FILE_NOT_FOUND, "Can't Open %s!", playbackWaveFile);
} }
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmioDescend(outputData->waveFile, (LPMMCKINFO)&mmckinfoParent, 0, MMIO_FINDRIFF)) if (mmioDescend(outputData->waveFile, (LPMMCKINFO)&mmckinfoParent, 0,
{ MMIO_FINDRIFF)) {
throwError(ERR_BAD_INPUT_FILE, "This file doesn't contain a WAVE!"); throwError(ERR_BAD_INPUT_FILE, "This file doesn't contain a WAVE!");
} }
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent,
{ MMIO_FINDCHUNK)) {
throwError(ERR_BAD_WAV_FORMAT, "Required fmt chunk was not found!"); 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) !=
if (mmioRead(outputData->waveFile, (HPSTR)format, mmckinfoSubchunk.cksize) != (LRESULT)mmckinfoSubchunk.cksize) (LRESULT)mmckinfoSubchunk.cksize) {
{
throwError(ERR_BAD_WAV_FORMAT, "Reading the fmt chunk!"); throwError(ERR_BAD_WAV_FORMAT, "Reading the fmt chunk!");
} }
if ((format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && if ((format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
(format->Samples.wValidBitsPerSample != format->Format.wBitsPerSample)) (format->Samples.wValidBitsPerSample != format->Format.wBitsPerSample)) {
{ throwError(ERR_NOT_SUPPORTED,
throwError(ERR_NOT_SUPPORTED, "Different container size and bit depth not supported"); "Different container size and bit depth not supported");
} }
mmioAscend(outputData->waveFile, &mmckinfoSubchunk, 0); mmioAscend(outputData->waveFile, &mmckinfoSubchunk, 0);
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent,
{ MMIO_FINDCHUNK)) {
throwError(ERR_BAD_WAV_FORMAT, "Reading the data chunk!"); throwError(ERR_BAD_WAV_FORMAT, "Reading the data chunk!");
} }
@ -206,45 +196,38 @@ int read_wav_file_header(char *playbackWaveFile, /* Input file name string */
outputData->bytesPerSample = outputData->bitsPerSample / 8; outputData->bytesPerSample = outputData->bitsPerSample / 8;
outputData->blockAlign = format->Format.nBlockAlign; outputData->blockAlign = format->Format.nBlockAlign;
if (format->Format.wFormatTag == WAVE_FORMAT_PCM) if (format->Format.wFormatTag == WAVE_FORMAT_PCM) {
{
outputData->waveFileFormat = FF_PCM; outputData->waveFileFormat = FF_PCM;
} } else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
outputData->waveFileFormat = FF_FLOAT32; outputData->waveFileFormat = FF_FLOAT32;
} } else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) {
{
if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
{
outputData->waveFileFormat = FF_PCM; outputData->waveFileFormat = FF_PCM;
} } else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
{
outputData->waveFileFormat == FF_FLOAT32; outputData->waveFileFormat == FF_FLOAT32;
} } else {
else throwError(ERR_NOT_SUPPORTED,
{ "Error: Unsupported WAVEFORMAT EXTENSIBLE SUBTYPE!");
throwError(ERR_NOT_SUPPORTED, "Error: Unsupported WAVEFORMAT EXTENSIBLE SUBTYPE!");
} }
} }
return (0); return (0);
} }
unsigned long read_entire_wav_file(UserData *outputData, /* Input options*/ unsigned long read_entire_wav_file(
UserData* outputData, /* Input options*/
void* audioData) /* Output data read form file */ 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) if (readCount != outputData->totalBytes) {
{ throwError(ERR_INCOMPLETE_INPUT_FILE,
throwError(ERR_INCOMPLETE_INPUT_FILE, "Failed to read all of audio data in wave file"); "Failed to read all of audio data in wave file");
} }
return (readCount); return (readCount);
} }
@ -254,35 +237,27 @@ int read_wav_file_header(char *playbackWaveFile, /* Input filename string */
UserData* outputData) /* Output options */ UserData* outputData) /* Output options */
{ {
uint32_t waveFormat; uint32_t waveFormat;
unsigned int i;
uint8_t tmpByte;
SF_INFO fileInfo; SF_INFO fileInfo;
unsigned long long readCount;
unsigned char *pByte;
outputData->waveFile = sf_open(playbackWaveFile, SFM_READ, &fileInfo); outputData->waveFile = sf_open(playbackWaveFile, SFM_READ, &fileInfo);
if (!outputData->waveFile) if (!outputData->waveFile) {
{
throwError(ERR_FILE_NOT_FOUND, "File %s not found\n", playbackWaveFile); throwError(ERR_FILE_NOT_FOUND, "File %s not found\n", playbackWaveFile);
} }
outputData->fs = fileInfo.samplerate; outputData->fs = fileInfo.samplerate;
outputData->numChannels = fileInfo.channels; outputData->numChannels = fileInfo.channels;
if ((waveFormat & SF_FORMAT_TYPEMASK) == SF_FORMAT_RF64) waveFormat = fileInfo.format;
{ if ((waveFormat & SF_FORMAT_TYPEMASK) == SF_FORMAT_RF64) {
throwError(ERR_NOT_SUPPORTED, "RF64 format not yet supported"); throwError(ERR_NOT_SUPPORTED, "RF64 format not yet supported");
} }
waveFormat = fileInfo.format;
if (((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) && if (((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) &&
((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX)) ((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX)) {
{
throwError(ERR_BAD_INPUT_FILE, "Input file is not a wavefile"); throwError(ERR_BAD_INPUT_FILE, "Input file is not a wavefile");
} }
switch(waveFormat & SF_FORMAT_SUBMASK) switch (waveFormat & SF_FORMAT_SUBMASK) {
{
case SF_FORMAT_PCM_16: case SF_FORMAT_PCM_16:
outputData->waveFileFormat = FF_PCM; outputData->waveFileFormat = FF_PCM;
outputData->bitsPerSample = 16; outputData->bitsPerSample = 16;
@ -310,36 +285,34 @@ int read_wav_file_header(char *playbackWaveFile, /* Input filename string */
return (0); return (0);
} }
unsigned long read_entire_wav_file(UserData *outputData, /* Input Options */ unsigned long read_entire_wav_file(
UserData* outputData, /* Input Options */
void* audioData) /* Output Audio data from file */ void* audioData) /* Output Audio data from file */
{ {
unsigned long readCount; unsigned long readCount;
readCount = sf_read_raw(outputData->waveFile, audioData, outputData->totalBytes); readCount =
if (readCount != outputData->totalBytes) 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"); throwError(ERR_INCOMPLETE_INPUT_FILE,
"Failed to read all of audio data in wave file");
} }
return (readCount); return (readCount);
} }
#endif #endif
unsigned int countBits(unsigned int a) {
unsigned int countBits(unsigned int a)
{
unsigned int count = 0; unsigned int count = 0;
while (a) while (a) {
{
count += (a & 0x1); count += (a & 0x1);
a >>= 1; a >>= 1;
} }
return (count); 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 am824Convert(UserData* userData, /* Input options */
void* audio, /* Input PCM samples */ void* audio, /* Input PCM samples */
@ -354,35 +327,30 @@ void am824Convert(UserData *userData, /* Input options */
unsigned long outputMemSize; unsigned long outputMemSize;
AM824ErrorCode err; AM824ErrorCode err;
if ((userData->bytesPerSample != 3) && (userData->bytesPerSample != 2)) {
if ((userData->bytesPerSample != 3) && (userData->bytesPerSample != 2)) throwError(ERR_NOT_SUPPORTED,
{ "Only 2 or 3 bytes per sample supported for AM824 mode");
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); AM824Framer framer(userData->numChannels, userData->bytesPerSample * 8,
if (err != AM824_ERR_OK) AM824_LITTLE_ENDIAN, err);
{ if (err != AM824_ERR_OK) {
if (err == AM824_ERR_UNSUPPORTED_BITDEPTH) if (err == AM824_ERR_UNSUPPORTED_BITDEPTH) {
{ throwError(ERR_NOT_SUPPORTED,
throwError(ERR_NOT_SUPPORTED, "AM824 framer reports bitdepth %d not supported", userData->bytesPerSample * 8); "AM824 framer reports bitdepth %d not supported",
userData->bytesPerSample * 8);
} }
} }
// framer.testCRC(); // framer.testCRC();
if (userData->am824_audio) if (userData->am824_audio) {
{
framer.setAudioMode(); framer.setAudioMode();
} } else {
else
{
framer.setDataMode(); framer.setDataMode();
} }
if (userData->am824_fs_match_wavfile) if (userData->am824_fs_match_wavfile) {
{ switch (userData->fs) {
switch(userData->fs)
{
case 32000: case 32000:
framer.setSamplingFrequency(FS_32000_HZ); framer.setSamplingFrequency(FS_32000_HZ);
break; break;
@ -395,35 +363,26 @@ void am824Convert(UserData *userData, /* Input options */
default: default:
framer.setSamplingFrequency(FS_NOT_INDICATED); framer.setSamplingFrequency(FS_NOT_INDICATED);
} }
} } else {
else
{
framer.setSamplingFrequency(userData->am824_fs); framer.setSamplingFrequency(userData->am824_fs);
} }
if (userData->am824_professional) if (userData->am824_professional) {
{
framer.setProfessionalMode(); framer.setProfessionalMode();
} } else {
else
{
framer.setConsumerMode(); framer.setConsumerMode();
} }
outputMemSize = (userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample; outputMemSize =
(userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample;
*am824audio = malloc(outputMemSize); *am824audio = malloc(outputMemSize);
outputPtr = (uint32_t*)*am824audio; outputPtr = (uint32_t*)*am824audio;
inputPtr = (uint8_t*)audio; inputPtr = (uint8_t*)audio;
while(bytesConverted < userData->totalBytes) while (bytesConverted < userData->totalBytes) {
{ for (channel = 0; channel < userData->numChannels; channel++) {
for (channel = 0 ; channel < userData->numChannels ; channel++) if (userData->bytesPerSample == 3) {
{
if (userData->bytesPerSample == 3)
{
inputSample32 = *((uint32_t*)inputPtr) & 0xffffff; inputSample32 = *((uint32_t*)inputPtr) & 0xffffff;
framer.getAM824Sample(inputSample32, (uint8_t*)outputPtr); framer.getAM824Sample(inputSample32, (uint8_t*)outputPtr);
} } else {
else
{
inputSample16 = *((uint16_t*)inputPtr); inputSample16 = *((uint16_t*)inputPtr);
framer.getAM824Sample(inputSample16, (uint8_t*)outputPtr); framer.getAM824Sample(inputSample16, (uint8_t*)outputPtr);
} }
@ -432,15 +391,14 @@ void am824Convert(UserData *userData, /* Input options */
bytesConverted += userData->bytesPerSample; bytesConverted += userData->bytesPerSample;
} }
} }
userData->totalBytes = (userData->totalBytes * sizeof(uint32_t))/userData->bytesPerSample; userData->totalBytes =
(userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample;
userData->bytesPerSample = sizeof(uint32_t); userData->bytesPerSample = sizeof(uint32_t);
userData->bitsPerSample = 32; userData->bitsPerSample = 32;
userData->blockAlign = userData->numChannels * userData->bytesPerSample; userData->blockAlign = userData->numChannels * userData->bytesPerSample;
} }
void list_devices(void) void list_devices(void) {
{
int i, numDevices, defaultDisplayed; int i, numDevices, defaultDisplayed;
const PaDeviceInfo* deviceInfo; const PaDeviceInfo* deviceInfo;
@ -448,27 +406,23 @@ void list_devices(void)
printf("Version text: '%s'\n", Pa_GetVersionText()); printf("Version text: '%s'\n", Pa_GetVersionText());
numDevices = Pa_GetDeviceCount(); numDevices = Pa_GetDeviceCount();
if (numDevices < 0) if (numDevices < 0) {
{
throwError(ERR_NO_DEVICE, "Pa_GetDeviceCount returned 0x%x\n", numDevices); throwError(ERR_NO_DEVICE, "Pa_GetDeviceCount returned 0x%x\n", numDevices);
} }
printf("Number of devices = %d\n", numDevices); printf("Number of devices = %d\n", numDevices);
for (i = 0; i < numDevices; i++) for (i = 0; i < numDevices; i++) {
{
deviceInfo = Pa_GetDeviceInfo(i); deviceInfo = Pa_GetDeviceInfo(i);
if (deviceInfo->maxOutputChannels > 0) { if (deviceInfo->maxOutputChannels > 0) {
printf("--------------------------------------- device #%d\n", i); printf("--------------------------------------- device #%d\n", i);
/* Mark global and API specific default devices */ /* Mark global and API specific default devices */
defaultDisplayed = 0; defaultDisplayed = 0;
if (i == Pa_GetDefaultOutputDevice()) if (i == Pa_GetDefaultOutputDevice()) {
{
printf("[ Default Output"); printf("[ Default Output");
defaultDisplayed = 1; defaultDisplayed = 1;
} } else if (i ==
else if (i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice) Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice) {
{
const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
printf("[ Default %s Output", hostInfo->name); printf("[ Default %s Output", hostInfo->name);
defaultDisplayed = 1; defaultDisplayed = 1;
@ -479,19 +433,25 @@ void list_devices(void)
/* print device info fields */ /* print device info fields */
#ifdef WIN32 #ifdef WIN32
{ /* Use wide char on windows, so we can show UTF-8 encoded device names */ { /* Use wide char on windows, so we can show UTF-8 encoded device names
*/
wchar_t wideName[MAX_PATH]; wchar_t wideName[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH - 1); MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName,
MAX_PATH - 1);
wprintf(L"Name = %s\n", wideName); wprintf(L"Name = %s\n", wideName);
} }
#else #else
printf("Name = %s\n", deviceInfo->name); printf("Name = %s\n", deviceInfo->name);
#endif #endif
printf("Host API = %s\n", Pa_GetHostApiInfo(deviceInfo->hostApi)->name); printf("Host API = %s\n",
printf("Max output channels = %d\n", deviceInfo->maxOutputChannels); 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 low output latency = %4.4f\n",
printf("Default high output latency = %4.4f\n", deviceInfo->defaultHighOutputLatency); deviceInfo->defaultLowOutputLatency);
printf("Default high output latency = %4.4f\n",
deviceInfo->defaultHighOutputLatency);
#ifdef WIN32 #ifdef WIN32
#if PA_USE_ASIO #if PA_USE_ASIO
@ -499,8 +459,8 @@ void list_devices(void)
if (Pa_GetHostApiInfo(deviceInfo->hostApi)->type == paASIO) { if (Pa_GetHostApiInfo(deviceInfo->hostApi)->type == paASIO) {
long minLatency, maxLatency, preferredLatency, granularity; long minLatency, maxLatency, preferredLatency, granularity;
err = PaAsio_GetAvailableLatencyValues(i, err = PaAsio_GetAvailableLatencyValues(i, &minLatency, &maxLatency,
&minLatency, &maxLatency, &preferredLatency, &granularity); &preferredLatency, &granularity);
printf("ASIO minimum buffer size = %ld\n", minLatency); printf("ASIO minimum buffer size = %ld\n", minLatency);
printf("ASIO maximum buffer size = %ld\n", maxLatency); printf("ASIO maximum buffer size = %ld\n", maxLatency);
@ -514,47 +474,56 @@ void list_devices(void)
#endif /* PA_USE_ASIO */ #endif /* PA_USE_ASIO */
#endif /* WIN32 */ #endif /* WIN32 */
printf("Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate); printf("Default sample rate = %8.2f\n",
deviceInfo->defaultSampleRate);
} }
} }
return; return;
} }
void print_usage(void) void print_usage(void) {
{ fprintf(stderr, "wavplay_am824 [OPTION]... <OUTPUT FILE> v%s\n",
fprintf(stderr, "wavplay_am824 [OPTION]... <OUTPUT FILE> v%s\n", VERSION_STRING); VERSION_STRING);
fprintf(stderr, "Copyright Dolby Laboratories Inc., 2020. All rights reserved.\n\n"); fprintf(stderr,
"Copyright Dolby Laboratories Inc., 2020. All rights reserved.\n\n");
fprintf(stderr, "-h Display this messgage\n"); fprintf(stderr, "-h Display this messgage\n");
fprintf(stderr, "-ld List playback devices\n"); fprintf(stderr, "-ld List playback devices\n");
fprintf(stderr, "-l Preferred playout latency in seconds\n"); fprintf(stderr,
fprintf(stderr, "-buf <samples> Playout buffer size in frames (samples x channels)\n"); "-l Preferred playout latency in seconds\n");
fprintf(stderr,
"-buf <samples> Playout buffer size in frames (samples x "
"channels)\n");
fprintf(stderr, "-d <index> Device index to use for playback\n"); fprintf(stderr, "-d <index> Device index to use for playback\n");
fprintf(stderr, "-am824 Using virtual sound card feeding an AM824/2110-31 stream\n"); fprintf(stderr,
fprintf(stderr, " The following keywords can follow the '-am824' switch to modify channel status:\n"); "-am824 Using virtual sound card feeding an "
fprintf(stderr, " audio, nonaudio, fs_not_indicated, fs_48k, fs_441k, fs_32k, professional, consumer\n"); "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) #if defined(_WIN32) || defined(_WIN64)
fprintf(stderr, "-e <index> Uses WASPI exclusive mode if selected device is a WASAPI device\n"); fprintf(stderr,
"-e <index> Uses WASPI exclusive mode if selected device "
"is a WASAPI device\n");
#endif #endif
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
/*******************************************************************/ /*******************************************************************/
int main(int argc, char *argv[]) int main(int argc, char* argv[]) {
{
PaStreamParameters outputParameters; PaStreamParameters outputParameters;
PaStream* stream; PaStream* stream;
PaError err = paNoError; PaError err = paNoError;
UserData data; UserData data;
unsigned int i; int i;
unsigned long bytesRead; unsigned long bytesRead;
unsigned int framesPerBuffer = 128; unsigned int framesPerBuffer = 128;
float userLatency = 0.0; float userLatency = 0.0;
char playbackWavFileName[256] = ""; char playbackWavFileName[256] = "";
unsigned int bitDepth = 16;
PaSampleFormat sampleFormat = paInt16;
double sampleRate = 48000.0; double sampleRate = 48000.0;
double startTime, finishTime; double startTime/*,finishTime*/;
void* audio; void* audio;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
struct PaWasapiStreamInfo wasapiInfo; struct PaWasapiStreamInfo wasapiInfo;
@ -563,35 +532,28 @@ int main(int argc, char *argv[])
unsigned int am824Mode = 0; unsigned int am824Mode = 0;
void* am824audio; void* am824audio;
outputParameters.device = paNoDevice; outputParameters.device = paNoDevice;
audio = NULL; audio = NULL;
err = Pa_Initialize(); err = Pa_Initialize();
if( err != paNoError ) if (err != paNoError) {
{ throwError(ERR_PORTAUDIO, "Pa_Initialize returned %d, %s", err,
throwError(ERR_PORTAUDIO, "Pa_Initialize returned %d, %s", err, Pa_GetErrorText(err)); Pa_GetErrorText(err));
} }
if (argc < 2) if (argc < 2) {
{
print_usage(); print_usage();
list_devices(); list_devices();
exit(0); exit(0);
} }
for (i = 1; i < (unsigned int)argc; i++) for (i = 1; i < argc; i++) {
{
// check to see if its a filename first // check to see if its a filename first
if ((argv[i][0] != '-') && (strlen(playbackWavFileName) == 0)) if ((argv[i][0] != '-') && (strlen(playbackWavFileName) == 0)) {
{
strcpy(playbackWavFileName, argv[i]); strcpy(playbackWavFileName, argv[i]);
} } else if (!strcmp(argv[i], "-d")) {
else if (!strcmp(argv[i], "-d")) if (i == (argc - 1)) {
{
if (i == (argc - 1))
{
print_usage(); print_usage();
throwError(ERR_NO_DEVICE, "Can't find soundcard index"); throwError(ERR_NO_DEVICE, "Can't find soundcard index");
} }
@ -601,20 +563,16 @@ int main(int argc, char *argv[])
i++; i++;
} }
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
else if (!strcmp(argv[i], "-e")) else if (!strcmp(argv[i], "-e")) {
{
waspiExclusiveMode = 1; waspiExclusiveMode = 1;
} }
#endif #endif
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") ||
{ !strcmp(argv[i], "--help")) {
print_usage(); print_usage();
exit(0); exit(0);
} } else if (!strcmp(argv[i], "-buf")) {
else if (!strcmp(argv[i], "-buf")) if (i == (argc - 1)) {
{
if (i == (argc - 1))
{
print_usage(); print_usage();
throwError(ERR_NO_DEVICE, "Can't find buffer size"); throwError(ERR_NO_DEVICE, "Can't find buffer size");
} }
@ -622,11 +580,8 @@ int main(int argc, char *argv[])
// We increment i here to step over the next parameter // We increment i here to step over the next parameter
// which has been parsed as the value // which has been parsed as the value
i++; i++;
} } else if (!strcmp(argv[i], "-l")) {
else if (!strcmp(argv[i], "-l")) if (i == (argc - 1)) {
{
if (i == (argc - 1))
{
print_usage(); print_usage();
throwError(ERR_NO_DEVICE, "Can't find latency"); throwError(ERR_NO_DEVICE, "Can't find latency");
} }
@ -634,111 +589,82 @@ int main(int argc, char *argv[])
// We increment i here to step over the next parameter // We increment i here to step over the next parameter
// which has been parsed as the value // which has been parsed as the value
i++; i++;
} } else if (!strcmp(argv[i], "-ld")) {
else if (!strcmp(argv[i], "-ld"))
{
list_devices(); list_devices();
exit(0); exit(0);
} } else if (!strcmp(argv[i], "-am824")) {
else if (!strcmp(argv[i], "-am824"))
{
am824Mode = 1; am824Mode = 1;
data.am824_audio = 1; data.am824_audio = 1;
data.am824_professional = 0; data.am824_professional = 0;
data.am824_fs_match_wavfile = 1; data.am824_fs_match_wavfile = 1;
i++; i++;
while((i < (unsigned int)argc) && (argv[i][0] != '-')) while ((i < argc) && (argv[i][0] != '-')) {
{ if (!strcmp(argv[i], "audio")) {
if (!strcmp(argv[i], "audio"))
{
data.am824_audio = 1; data.am824_audio = 1;
} } else if (!strcmp(argv[i], "nonaudio")) {
else if (!strcmp(argv[i], "nonaudio"))
{
data.am824_audio = 0; data.am824_audio = 0;
} } else if (!strcmp(argv[i], "fs_not_indicated")) {
else if (!strcmp(argv[i], "fs_not_indicated"))
{
data.am824_fs = FS_NOT_INDICATED; data.am824_fs = FS_NOT_INDICATED;
data.am824_fs_match_wavfile = 0; data.am824_fs_match_wavfile = 0;
} } else if (!strcmp(argv[i], "fs_48k")) {
else if (!strcmp(argv[i], "fs_48k"))
{
data.am824_fs = FS_48000_HZ; data.am824_fs = FS_48000_HZ;
data.am824_fs_match_wavfile = 0; data.am824_fs_match_wavfile = 0;
} } else if (!strcmp(argv[i], "fs_441k")) {
else if (!strcmp(argv[i], "fs_441k"))
{
data.am824_fs = FS_44100_HZ; data.am824_fs = FS_44100_HZ;
data.am824_fs_match_wavfile = 0; data.am824_fs_match_wavfile = 0;
} } else if (!strcmp(argv[i], "fs_32k")) {
else if (!strcmp(argv[i], "fs_32k"))
{
data.am824_fs = FS_32000_HZ; data.am824_fs = FS_32000_HZ;
data.am824_fs_match_wavfile = 0; data.am824_fs_match_wavfile = 0;
} } else if (!strcmp(argv[i], "professional")) {
else if (!strcmp(argv[i], "professional"))
{
data.am824_professional = 1; data.am824_professional = 1;
} } else if (!strcmp(argv[i], "consumer")) {
else if (!strcmp(argv[i], "consumer"))
{
data.am824_professional = 0; data.am824_professional = 0;
} } else {
else
{
break; break;
} }
i++; i++;
} }
i--; i--;
} } else {
else
{
print_usage(); print_usage();
throwError(ERR_BAD_CMD_OPTION, "Option %s not recognized", argv[i]); throwError(ERR_BAD_CMD_OPTION, "Option %s not recognized", argv[i]);
} }
} }
if (read_wav_file_header(playbackWavFileName, &data)) if (read_wav_file_header(playbackWavFileName, &data)) {
{
throwError(ERR_BAD_WAV_FORMAT, "Bad wav header"); throwError(ERR_BAD_WAV_FORMAT, "Bad wav header");
} }
audio = malloc(data.totalBytes); audio = malloc(data.totalBytes);
if (!audio) if (!audio) {
{
throwError(ERR_NO_MEMORY, "Memory allocation failed"); throwError(ERR_NO_MEMORY, "Memory allocation failed");
} }
bytesRead = read_entire_wav_file(&data, audio); bytesRead = read_entire_wav_file(&data, audio);
if (bytesRead != data.totalBytes) if (bytesRead != data.totalBytes) {
{
throwError(ERR_INCOMPLETE_INPUT_FILE, "Couldn't read all input data"); throwError(ERR_INCOMPLETE_INPUT_FILE, "Couldn't read all input data");
} }
if (am824Mode) if (am824Mode) {
{
printf("AM824 output mode selected\n"); printf("AM824 output mode selected\n");
am824Convert(&data, audio, &am824audio); am824Convert(&data, audio, &am824audio);
free(audio); free(audio);
audio = am824audio; audio = am824audio;
} }
if (outputParameters.device == paNoDevice) if (outputParameters.device == paNoDevice) {
{
outputParameters.device = Pa_GetDefaultOutputDevice(); outputParameters.device = Pa_GetDefaultOutputDevice();
} }
if (outputParameters.device == paNoDevice) if (outputParameters.device == paNoDevice) {
{
throwError(ERR_NO_DEVICE, "No default output device"); throwError(ERR_NO_DEVICE, "No default output device");
} }
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
if ((waspiExclusiveMode) && if ((waspiExclusiveMode) &&
(Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->type == paWASAPI)) { (Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)
->type == paWASAPI)) {
wasapiInfo.size = sizeof(PaWasapiStreamInfo); wasapiInfo.size = sizeof(PaWasapiStreamInfo);
wasapiInfo.hostApiType = paWASAPI; wasapiInfo.hostApiType = paWASAPI;
wasapiInfo.version = 1; wasapiInfo.version = 1;
@ -749,152 +675,134 @@ int main(int argc, char *argv[])
wasapiInfo.threadPriority = eThreadPriorityProAudio; wasapiInfo.threadPriority = eThreadPriorityProAudio;
outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo); outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo);
printf("Detected WASAPI device and setting exclusive mode\n"); printf("Detected WASAPI device and setting exclusive mode\n");
} } else {
else
{
#endif #endif
outputParameters.hostApiSpecificStreamInfo = NULL; outputParameters.hostApiSpecificStreamInfo = NULL;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
} }
#endif #endif
if (userLatency > 0.0) if (userLatency > 0.0) {
{
outputParameters.suggestedLatency = userLatency; outputParameters.suggestedLatency = userLatency;
} } else {
else outputParameters.suggestedLatency =
{ Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
} }
switch(data.waveFileFormat) switch (data.waveFileFormat) {
{
case FF_PCM: case FF_PCM:
if ((data.bitsPerSample != 8) && (data.bitsPerSample != 16) if ((data.bitsPerSample != 8) && (data.bitsPerSample != 16) &&
&& (data.bitsPerSample != 24) && (data.bitsPerSample != 32)) (data.bitsPerSample != 24) && (data.bitsPerSample != 32)) {
{
throwError(ERR_NOT_SUPPORTED, "Unsupported WAVE_FORMAT_PCM bitdepth"); throwError(ERR_NOT_SUPPORTED, "Unsupported WAVE_FORMAT_PCM bitdepth");
} }
break; break;
case FF_FLOAT32: case FF_FLOAT32:
if (data.bitsPerSample != 32) if (data.bitsPerSample != 32) {
{ throwError(
throwError(ERR_BAD_WAV_FORMAT, "Wavefile indicated floating point but bits per sample is not 32"); ERR_BAD_WAV_FORMAT,
"Wavefile indicated floating point but bits per sample is not 32");
} }
break; break;
default: default:
throwError(ERR_NOT_SUPPORTED, "Unsupported WAV format tag (WAVE_FORMAT_PCM & WAVE_FORMAT_IEEE_FLOAT supported)"); throwError(ERR_NOT_SUPPORTED,
"Unsupported WAV format tag (WAVE_FORMAT_PCM & "
"WAVE_FORMAT_IEEE_FLOAT supported)");
} }
if (data.bitsPerSample == 16) if (data.bitsPerSample == 16) {
{
outputParameters.sampleFormat = paInt16; outputParameters.sampleFormat = paInt16;
} } else if (data.bitsPerSample == 8) {
else if (data.bitsPerSample == 8)
{
outputParameters.sampleFormat = paInt8; outputParameters.sampleFormat = paInt8;
} } else if (data.bitsPerSample == 24) {
else if (data.bitsPerSample == 24)
{
outputParameters.sampleFormat = paInt24; outputParameters.sampleFormat = paInt24;
} } else if (data.bitsPerSample == 32) {
else if (data.bitsPerSample == 32) if (data.waveFileFormat == FF_FLOAT32) {
{
if (data.waveFileFormat == FF_FLOAT32)
{
outputParameters.sampleFormat = paFloat32; outputParameters.sampleFormat = paFloat32;
} } else {
else
{
outputParameters.sampleFormat = paInt32; outputParameters.sampleFormat = paInt32;
} }
} } else {
else throwError(ERR_NOT_SUPPORTED, "Unsupported bitdepth %d",
{ data.bitsPerSample);
throwError(ERR_NOT_SUPPORTED, "Unsupported bitdepth %d", data.bitsPerSample);
} }
outputParameters.channelCount = data.numChannels; outputParameters.channelCount = data.numChannels;
sampleRate = (double)data.fs; sampleRate = (double)data.fs;
err = Pa_IsFormatSupported(NULL, &outputParameters, sampleRate); err = Pa_IsFormatSupported(NULL, &outputParameters, sampleRate);
if (err != paNoError) if (err != paNoError) {
{ throwError(ERR_NOT_SUPPORTED, "Pa_IsFormatSupported returned %d, %s", err,
throwError(ERR_NOT_SUPPORTED, "Pa_IsFormatSupported returned %d, %s", err, Pa_GetErrorText(err)); Pa_GetErrorText(err));
} }
printf("Pa_IsFormatSupported succeeded\n"); 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); 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 // Set callback parameters
data.frameIndex = 0; /* Index into sample array. */ data.frameIndex = 0; /* Index into sample array. */
data.maxFrameIndex = (unsigned long)data.totalBytes / data.blockAlign; data.maxFrameIndex = (unsigned long)data.totalBytes / data.blockAlign;
data.audio = (unsigned char*)audio; data.audio = (unsigned char*)audio;
printf("\n=== Now playing back. ===\n"); fflush(stdout); printf("\n=== Now playing back. ===\n");
err = Pa_OpenStream( fflush(stdout);
&stream, err = Pa_OpenStream(&stream, NULL, /* no input */
NULL, /* no input */ &outputParameters, data.fs, framesPerBuffer,
&outputParameters, paClipOff, /* we won't output out of range samples so
data.fs, don't bother clipping them */
framesPerBuffer, playCallback, &data);
paClipOff, /* we won't output out of range samples so don't bother clipping them */ if (err != paNoError) {
playCallback,
&data );
if( err != paNoError )
{
Pa_Terminate(); Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa_OpenStream returned %d, %s", err, Pa_GetErrorText( err )); throwError(ERR_NO_STREAM, "Pa_OpenStream returned %d, %s", err,
Pa_GetErrorText(err));
} }
if( stream ) if (stream) {
{
#if defined(__linux__) #if defined(__linux__)
PaAlsa_EnableRealtimeScheduling(stream, 1); PaAlsa_EnableRealtimeScheduling(stream, 1);
printf("RealTime Scheduling enabled\n"); printf("RealTime Scheduling enabled\n");
#endif #endif
err = Pa_StartStream(stream); err = Pa_StartStream(stream);
if( err != paNoError ) if (err != paNoError) {
{
Pa_Terminate(); Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa_StartStream returned %d, %s", err, Pa_GetErrorText( err )); throwError(ERR_NO_STREAM, "Pa_StartStream returned %d, %s", err,
Pa_GetErrorText(err));
} }
printf("Waiting for playback to start.\n"); printf("Waiting for playback to start.\n");
do do {
{
startTime = Pa_GetStreamTime(stream); startTime = Pa_GetStreamTime(stream);
} } while (startTime == 0.0);
while (startTime == 0.0);
printf("Waiting for playback to finish.\n"); printf("Waiting for playback to finish.\n");
fflush(stdout); fflush(stdout);
finishTime = data.maxFrameIndex / data.fs; //finishTime = data.maxFrameIndex / data.fs;
while ((err = Pa_IsStreamActive(stream)) == 1) while ((err = Pa_IsStreamActive(stream)) == 1) {
{
Pa_Sleep(1000); Pa_Sleep(1000);
} }
printf("\n"); printf("\n");
if( err != paNoError ) if (err != paNoError) {
{
Pa_Terminate(); Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa error %d received during playback: %s", err, Pa_GetErrorText( err )); throwError(ERR_NO_STREAM, "Pa error %d received during playback: %s", err,
Pa_GetErrorText(err));
} }
err = Pa_CloseStream(stream); err = Pa_CloseStream(stream);
if (err != paNoError) if (err != paNoError)
printf("Done.\n");
printf("Done.\n"); fflush(stdout); fflush(stdout);
} }
if (audio) if (audio) {
{
free(audio); free(audio);
} }
Pa_Terminate(); Pa_Terminate();
exit(0); exit(0);
} }