diff --git a/wavplay_am824/wavplay_am824.cpp b/wavplay_am824/wavplay_am824.cpp
new file mode 100644
index 0000000..3f12741
--- /dev/null
+++ b/wavplay_am824/wavplay_am824.cpp
@@ -0,0 +1,900 @@
+/************************************************************************************************************
+ * WavPlay AM824
+ * Copyright (C) 2020, Dolby Laboratories Inc.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ************************************************************************************************************/
+
+#define VERSION_STRING "1.0"
+
+#include
+#include
+#include
+#include
+#include
+#include "portaudio.h"
+#include "am824_framer.h"
+
+#if defined(__linux__ )
+#include "pa_linux_alsa.h"
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+#include "pa_win_wasapi.h"
+
+#include
+#include
+#include
+#include
+
+#if PA_USE_ASIO
+#include "pa_asio.h"
+#endif
+#else
+#include
+#endif
+
+typedef enum
+{
+ FF_PCM = 0,
+ FF_FLOAT32,
+}
+FileFormat;
+
+
+typedef enum
+{
+ ERR_FILE_NOT_FOUND = -101,
+ ERR_BAD_INPUT_FILE,
+ ERR_BAD_WAV_FORMAT,
+ ERR_INCOMPLETE_INPUT_FILE,
+ ERR_NOT_SUPPORTED,
+ ERR_BAD_CMD_OPTION,
+ ERR_NO_MEMORY,
+ ERR_NO_DEVICE,
+ ERR_NO_STREAM,
+ ERR_PORTAUDIO,
+ ERR_OK = 0
+}
+ErrorCode;
+
+
+typedef struct
+{
+ unsigned long frameIndex; /* Index into sample array. */
+ unsigned long maxFrameIndex;
+ unsigned int numChannels;
+ unsigned int bytesPerSample;
+ unsigned char *audio;
+ FileFormat waveFileFormat;
+ unsigned int bitsPerSample;
+ unsigned int fs;
+ unsigned int blockAlign;
+ unsigned long totalBytes;
+#if defined(_WIN32) || defined(_WIN64)
+ HMMIO waveFile;
+#else
+ SNDFILE *waveFile;
+#endif
+ unsigned int am824_audio;
+ AM824SamplingFrequency am824_fs;
+ unsigned int am824_fs_match_wavfile;
+ unsigned int am824_professional;
+}
+UserData;
+
+void throwError(ErrorCode err, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ fprintf(stderr, "***Error: ");
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ exit(err);
+}
+
+/* This is the main callback functioned called by PortAudio. It is registered when the stream is created */
+
+static int playCallback( const void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ UserData *data = (UserData*)userData;
+ unsigned char *rptr = &data->audio[data->frameIndex * data->numChannels * data->bytesPerSample];
+ unsigned char *wptr = (unsigned char *)outputBuffer;
+ unsigned int i;
+ int finished;
+ unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+
+ (void) inputBuffer; /* Prevent unused variable warnings. */
+ (void) timeInfo;
+ (void) statusFlags;
+ (void) userData;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ /* final buffer... */
+ for( i=0; inumChannels * data->bytesPerSample; i++)
+ {
+ *wptr++ = *rptr++;
+ }
+ for( ; inumChannels * data->bytesPerSample; i++ )
+ {
+ *wptr++ = 0;
+ }
+ data->frameIndex += framesLeft;
+ finished = paComplete;
+ }
+ else
+ {
+ for( i=0; i< framesPerBuffer * data->numChannels * data->bytesPerSample; i++)
+ {
+ *wptr++ = *rptr++;
+ }
+ data->frameIndex += framesPerBuffer;
+ finished = paContinue;
+ }
+ return finished;
+}
+
+/* Two versions of the following two functions exist, one for Windows and one for Linux. This approach was chosen
+ because a platform independent wav file library was not used but rather Windows API and libasound directly */
+
+#if defined(_WIN32) || defined(_WIN64)
+int read_wav_file_header(char *playbackWaveFile, /* Input file name string */
+ UserData *outputData) /* Output options */
+{
+ MMCKINFO mmckinfoParent;
+ MMCKINFO mmckinfoSubchunk;
+ WAVEFORMATEXTENSIBLE *format;
+
+ outputData->waveFile = mmioOpenA(playbackWaveFile, 0, MMIO_READ | MMIO_ALLOCBUF);
+ if (!outputData->waveFile)
+ {
+ throwError(ERR_FILE_NOT_FOUND, "Can't Open %s!", playbackWaveFile);
+ }
+ mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
+ if (mmioDescend(outputData->waveFile, (LPMMCKINFO)&mmckinfoParent, 0, MMIO_FINDRIFF))
+ {
+ throwError(ERR_BAD_INPUT_FILE, "This file doesn't contain a WAVE!");
+ }
+ mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
+ if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
+ {
+ throwError(ERR_BAD_WAV_FORMAT, "Required fmt chunk was not found!");
+ }
+
+ format = (WAVEFORMATEXTENSIBLE *)malloc(mmckinfoSubchunk.cksize);
+
+
+ if (mmioRead(outputData->waveFile, (HPSTR)format, mmckinfoSubchunk.cksize) != (LRESULT)mmckinfoSubchunk.cksize)
+ {
+ throwError(ERR_BAD_WAV_FORMAT, "Reading the fmt chunk!");
+ }
+
+ if ((format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
+ (format->Samples.wValidBitsPerSample != format->Format.wBitsPerSample))
+ {
+ throwError(ERR_NOT_SUPPORTED, "Different container size and bit depth not supported");
+ }
+
+ mmioAscend(outputData->waveFile, &mmckinfoSubchunk, 0);
+ mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
+ if (mmioDescend(outputData->waveFile, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
+ {
+ throwError(ERR_BAD_WAV_FORMAT, "Reading the data chunk!");
+ }
+
+ outputData->fs = format->Format.nSamplesPerSec;
+ outputData->numChannels = format->Format.nChannels;
+ outputData->totalBytes = mmckinfoSubchunk.cksize;
+ outputData->bitsPerSample = format->Format.wBitsPerSample;
+ outputData->bytesPerSample = outputData->bitsPerSample / 8;
+ outputData->blockAlign = format->Format.nBlockAlign;
+
+ if (format->Format.wFormatTag == WAVE_FORMAT_PCM)
+ {
+ outputData->waveFileFormat = FF_PCM;
+ }
+ else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+ {
+ outputData->waveFileFormat = FF_FLOAT32;
+ }
+ else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+ {
+ if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
+ {
+ outputData->waveFileFormat = FF_PCM;
+ }
+ else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
+ {
+ outputData->waveFileFormat == FF_FLOAT32;
+ }
+ else
+ {
+ throwError(ERR_NOT_SUPPORTED, "Error: Unsupported WAVEFORMAT EXTENSIBLE SUBTYPE!");
+ }
+ }
+
+ return(0);
+}
+
+unsigned long read_entire_wav_file(UserData *outputData, /* Input options*/
+ void *audioData) /* Output data read form file */
+{
+ unsigned long readCount;
+
+ readCount = mmioRead(outputData->waveFile, (char *)audioData, outputData->totalBytes);
+
+ mmioClose(outputData->waveFile, 0);
+
+ if (readCount != outputData->totalBytes)
+ {
+ throwError(ERR_INCOMPLETE_INPUT_FILE, "Failed to read all of audio data in wave file");
+ }
+ return(readCount);
+}
+
+#else
+int read_wav_file_header(char *playbackWaveFile, /* Input filename string */
+ UserData *outputData) /* Output options */
+{
+ uint32_t waveFormat;
+ unsigned int i;
+ uint8_t tmpByte;
+ SF_INFO fileInfo;
+ unsigned long long readCount;
+ unsigned char *pByte;
+
+ outputData->waveFile = sf_open(playbackWaveFile, SFM_READ, &fileInfo);
+ if (!outputData->waveFile)
+ {
+ throwError(ERR_FILE_NOT_FOUND, "File %s not found\n", playbackWaveFile);
+ }
+
+ outputData->fs = fileInfo.samplerate;
+ outputData->numChannels = fileInfo.channels;
+
+ if ((waveFormat & SF_FORMAT_TYPEMASK) == SF_FORMAT_RF64)
+ {
+ throwError(ERR_NOT_SUPPORTED, "RF64 format not yet supported");
+ }
+
+ waveFormat = fileInfo.format;
+ if (((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) &&
+ ((waveFormat & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX))
+ {
+ throwError(ERR_BAD_INPUT_FILE, "Input file is not a wavefile");
+ }
+
+ switch(waveFormat & SF_FORMAT_SUBMASK)
+ {
+ case SF_FORMAT_PCM_16:
+ outputData->waveFileFormat = FF_PCM;
+ outputData->bitsPerSample = 16;
+ break;
+ case SF_FORMAT_PCM_24:
+ outputData->waveFileFormat = FF_PCM;
+ outputData->bitsPerSample = 24;
+ break;
+ case SF_FORMAT_PCM_32:
+ outputData->waveFileFormat = FF_PCM;
+ outputData->bitsPerSample = 32;
+ break;
+ case SF_FORMAT_FLOAT:
+ outputData->waveFileFormat = FF_FLOAT32;
+ outputData->bitsPerSample = 32;
+ break;
+ default:
+ throwError(ERR_NOT_SUPPORTED, "Unsupported wavefile format supported");
+ }
+
+ outputData->bytesPerSample = outputData->bitsPerSample / 8;
+ outputData->blockAlign = outputData->numChannels * outputData->bytesPerSample;
+ outputData->totalBytes = outputData->blockAlign * fileInfo.frames;
+
+ return(0);
+}
+
+unsigned long read_entire_wav_file(UserData *outputData, /* Input Options */
+ void *audioData) /* Output Audio data from file */
+{
+ unsigned long readCount;
+
+ readCount = sf_read_raw(outputData->waveFile, audioData, outputData->totalBytes);
+ if (readCount != outputData->totalBytes)
+ {
+ throwError(ERR_INCOMPLETE_INPUT_FILE, "Failed to read all of audio data in wave file");
+ }
+ return(readCount);
+}
+
+
+#endif
+
+
+
+unsigned int countBits(unsigned int a)
+{
+ unsigned int count = 0;
+ while (a)
+ {
+ count += (a & 0x1);
+ a >>= 1;
+ }
+ return(count);
+}
+
+/* This function takes standard PCM audio plus channel status options and creates the samples for AM824 format */
+
+void am824Convert(UserData *userData, /* Input options */
+ void *audio, /* Input PCM samples */
+ void **am824audio) /* Output AM824 samples, always 32bit */
+{
+ unsigned long bytesConverted = 0;
+ uint32_t inputSample32;
+ uint16_t inputSample16;
+ uint8_t *inputPtr;
+ uint32_t *outputPtr;
+ unsigned int channel;
+ unsigned long outputMemSize;
+ AM824ErrorCode err;
+
+
+ if ((userData->bytesPerSample != 3) && (userData->bytesPerSample != 2))
+ {
+ throwError(ERR_NOT_SUPPORTED, "Only 2 or 3 bytes per sample supported for AM824 mode");
+ }
+
+ AM824Framer framer(userData->numChannels, userData->bytesPerSample * 8, AM824_LITTLE_ENDIAN, err);
+ if (err != AM824_ERR_OK)
+ {
+ if (err == AM824_ERR_UNSUPPORTED_BITDEPTH)
+ {
+ throwError(ERR_NOT_SUPPORTED, "AM824 framer reports bitdepth %d not supported", userData->bytesPerSample * 8);
+ }
+ }
+
+ //framer.testCRC();
+
+ if (userData->am824_audio)
+ {
+ framer.setAudioMode();
+ }
+ else
+ {
+ framer.setDataMode();
+ }
+ if (userData->am824_fs_match_wavfile)
+ {
+ switch(userData->fs)
+ {
+ case 32000:
+ framer.setSamplingFrequency(FS_32000_HZ);
+ break;
+ case 44100:
+ framer.setSamplingFrequency(FS_44100_HZ);
+ break;
+ case 48000:
+ framer.setSamplingFrequency(FS_48000_HZ);
+ break;
+ default:
+ framer.setSamplingFrequency(FS_NOT_INDICATED);
+ }
+ }
+ else
+ {
+ framer.setSamplingFrequency(userData->am824_fs);
+ }
+ if (userData->am824_professional)
+ {
+ framer.setProfessionalMode();
+ }
+ else
+ {
+ framer.setConsumerMode();
+ }
+ outputMemSize = (userData->totalBytes * sizeof(uint32_t)) / userData->bytesPerSample;
+ *am824audio = malloc(outputMemSize);
+ outputPtr = (uint32_t *) *am824audio;
+ inputPtr = (uint8_t *)audio;
+
+ while(bytesConverted < userData->totalBytes)
+ {
+ for (channel = 0 ; channel < userData->numChannels ; channel++)
+ {
+ if (userData->bytesPerSample == 3)
+ {
+ inputSample32 = *((uint32_t *)inputPtr) & 0xffffff;
+ framer.getAM824Sample(inputSample32, (uint8_t *)outputPtr);
+ }
+ else
+ {
+ inputSample16 = *((uint16_t *)inputPtr);
+ framer.getAM824Sample(inputSample16, (uint8_t *)outputPtr);
+ }
+ outputPtr += 1;
+ inputPtr += userData->bytesPerSample;
+ bytesConverted += userData->bytesPerSample;
+ }
+ }
+ userData->totalBytes = (userData->totalBytes * sizeof(uint32_t))/userData->bytesPerSample;
+ userData->bytesPerSample = sizeof(uint32_t);
+ userData->bitsPerSample = 32;
+ userData->blockAlign = userData->numChannels * userData->bytesPerSample;
+
+}
+
+void list_devices(void)
+{
+ int i, numDevices, defaultDisplayed;
+ const PaDeviceInfo *deviceInfo;
+
+ printf("PortAudio version: 0x%08X\n", Pa_GetVersion());
+ printf("Version text: '%s'\n", Pa_GetVersionText());
+
+ numDevices = Pa_GetDeviceCount();
+ if (numDevices < 0)
+ {
+ throwError(ERR_NO_DEVICE, "Pa_GetDeviceCount returned 0x%x\n", numDevices);
+ }
+
+ printf("Number of devices = %d\n", numDevices);
+ for (i = 0; i < numDevices; i++)
+ {
+ deviceInfo = Pa_GetDeviceInfo(i);
+ if (deviceInfo->maxOutputChannels > 0) {
+ printf("--------------------------------------- device #%d\n", i);
+
+ /* Mark global and API specific default devices */
+ defaultDisplayed = 0;
+ if (i == Pa_GetDefaultOutputDevice())
+ {
+ printf("[ Default Output");
+ defaultDisplayed = 1;
+ }
+ else if (i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice)
+ {
+ const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
+ printf("[ Default %s Output", hostInfo->name);
+ defaultDisplayed = 1;
+ }
+
+ if (defaultDisplayed)
+ printf(" ]\n");
+
+ /* print device info fields */
+#ifdef WIN32
+ { /* Use wide char on windows, so we can show UTF-8 encoded device names */
+ wchar_t wideName[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH - 1);
+ wprintf(L"Name = %s\n", wideName);
+ }
+#else
+ printf("Name = %s\n", deviceInfo->name);
+#endif
+ printf("Host API = %s\n", Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
+ printf("Max output channels = %d\n", deviceInfo->maxOutputChannels);
+
+ printf("Default low output latency = %4.4f\n", deviceInfo->defaultLowOutputLatency);
+ printf("Default high output latency = %4.4f\n", deviceInfo->defaultHighOutputLatency);
+
+#ifdef WIN32
+#if PA_USE_ASIO
+ /* ASIO specific latency information */
+ if (Pa_GetHostApiInfo(deviceInfo->hostApi)->type == paASIO) {
+ long minLatency, maxLatency, preferredLatency, granularity;
+
+ err = PaAsio_GetAvailableLatencyValues(i,
+ &minLatency, &maxLatency, &preferredLatency, &granularity);
+
+ printf("ASIO minimum buffer size = %ld\n", minLatency);
+ printf("ASIO maximum buffer size = %ld\n", maxLatency);
+ printf("ASIO preferred buffer size = %ld\n", preferredLatency);
+
+ if (granularity == -1)
+ printf("ASIO buffer granularity = power of 2\n");
+ else
+ printf("ASIO buffer granularity = %ld\n", granularity);
+ }
+#endif /* PA_USE_ASIO */
+#endif /* WIN32 */
+
+ printf("Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate);
+ }
+ }
+ return;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "wavplay_am824 [OPTION]...