Minor changes to AM824 WAV player before merge into master branch
This commit is contained in:
parent
d7a117e482
commit
344fba0827
157
wavplay_am824/.clang-format
Normal file
157
wavplay_am824/.clang-format
Normal 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
|
||||||
|
...
|
||||||
|
|
8
wavplay_am824/CMakeLists.txt
Normal file
8
wavplay_am824/CMakeLists.txt
Normal 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
12
wavplay_am824/README.md
Normal 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
|
||||||
|
|
@ -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]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
Binary file not shown.
9
wavplay_am824/ubuntu-packages.sh
Executable file
9
wavplay_am824/ubuntu-packages.sh
Executable 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
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user