Initial commit

This player is not fully tested due to occasional drops on the output caused CRC errors when used with encoded audio such as Dolby E. However these drops have been observed with aplay when used in memory mapped mode so it is not believed to be an issue with this player. However, final testing is still outstanding
This commit is contained in:
James Cowdery 2020-07-30 12:38:40 -07:00 committed by GitHub
parent 23c6888b6c
commit bc0e1897c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 1065 additions and 0 deletions

View File

@ -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 <http://www.gnu.org/licenses/>
************************************************************************************************************/
#define VERSION_STRING "1.0"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#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 <windows.h>
#include <Ks.h>
#include <Ksmedia.h>
#include <mmreg.h>
#if PA_USE_ASIO
#include "pa_asio.h"
#endif
#else
#include <sndfile.h>
#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; i<framesLeft * data->numChannels * data->bytesPerSample; i++)
{
*wptr++ = *rptr++;
}
for( ; i<framesPerBuffer * data->numChannels * 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]... <OUTPUT FILE> v%s\n", VERSION_STRING);
fprintf(stderr, "Copyright Dolby Laboratories Inc., 2020. All rights reserved.\n\n");
fprintf(stderr, "-h Display this messgage\n");
fprintf(stderr, "-ld List playback devices\n");
fprintf(stderr, "-l Preferred playout latency in seconds\n");
fprintf(stderr, "-buf <samples> Playout buffer size in frames (samples x channels)\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, " The following keywords can follow the '-am824' switch to modify channel status:\n");
fprintf(stderr, " audio, nonaudio, fs_not_indicated, fs_48k, fs_441k, fs_32k, professional, consumer\n");
#if defined(_WIN32) || defined(_WIN64)
fprintf(stderr, "-e <index> Uses WASPI exclusive mode if selected device is a WASAPI device\n");
#endif
fprintf(stderr, "\n");
}
/*******************************************************************/
int main(int argc, char *argv[])
{
PaStreamParameters outputParameters;
PaStream* stream;
PaError err = paNoError;
UserData data;
unsigned int i;
unsigned long bytesRead;
unsigned int framesPerBuffer = 128;
float userLatency = 0.0;
char playbackWavFileName[256] = "";
unsigned int bitDepth = 16;
PaSampleFormat sampleFormat = paInt16;
double sampleRate = 48000.0;
double startTime, finishTime;
void *audio;
#if defined(_WIN32) || defined(_WIN64)
struct PaWasapiStreamInfo wasapiInfo;
int waspiExclusiveMode = 0;
#endif
unsigned int am824Mode = 0;
void *am824audio;
outputParameters.device = paNoDevice;
audio = NULL;
err = Pa_Initialize();
if( err != paNoError )
{
throwError(ERR_PORTAUDIO, "Pa_Initialize returned %d, %s", err, Pa_GetErrorText(err));
}
if (argc < 2)
{
print_usage();
list_devices();
exit(0);
}
for (i = 1; i < (unsigned int)argc; i++)
{
// check to see if its a filename first
if ((argv[i][0] != '-') && (strlen(playbackWavFileName) == 0))
{
strcpy(playbackWavFileName, argv[i]);
}
else if (!strcmp(argv[i], "-d"))
{
if (i == (argc - 1))
{
print_usage();
throwError(ERR_NO_DEVICE, "Can't find soundcard index");
}
outputParameters.device = atoi(argv[i + 1]);
// We increment i here to step over the next parameter
// which has been parsed as the value
i++;
}
#if defined(_WIN32) || defined(_WIN64)
else if (!strcmp(argv[i], "-e"))
{
waspiExclusiveMode = 1;
}
#endif
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help"))
{
print_usage();
exit(0);
}
else if (!strcmp(argv[i], "-buf"))
{
if (i == (argc - 1))
{
print_usage();
throwError(ERR_NO_DEVICE, "Can't find buffer size");
}
framesPerBuffer = atoi(argv[i + 1]);
// We increment i here to step over the next parameter
// which has been parsed as the value
i++;
}
else if (!strcmp(argv[i], "-l"))
{
if (i == (argc - 1))
{
print_usage();
throwError(ERR_NO_DEVICE, "Can't find latency");
}
userLatency = atof(argv[i + 1]);
// We increment i here to step over the next parameter
// which has been parsed as the value
i++;
}
else if (!strcmp(argv[i], "-ld"))
{
list_devices();
exit(0);
}
else if (!strcmp(argv[i], "-am824"))
{
am824Mode = 1;
data.am824_audio = 1;
data.am824_professional = 0;
data.am824_fs_match_wavfile = 1;
i++;
while((i < (unsigned int)argc) && (argv[i][0] != '-'))
{
if (!strcmp(argv[i], "audio"))
{
data.am824_audio=1;
}
else if (!strcmp(argv[i], "nonaudio"))
{
data.am824_audio=0;
}
else if (!strcmp(argv[i], "fs_not_indicated"))
{
data.am824_fs = FS_NOT_INDICATED;
data.am824_fs_match_wavfile = 0;
}
else if (!strcmp(argv[i], "fs_48k"))
{
data.am824_fs = FS_48000_HZ;
data.am824_fs_match_wavfile = 0;
}
else if (!strcmp(argv[i], "fs_441k"))
{
data.am824_fs = FS_44100_HZ;
data.am824_fs_match_wavfile = 0;
}
else if (!strcmp(argv[i], "fs_32k"))
{
data.am824_fs = FS_32000_HZ;
data.am824_fs_match_wavfile = 0;
}
else if (!strcmp(argv[i], "professional"))
{
data.am824_professional=1;
}
else if (!strcmp(argv[i], "consumer"))
{
data.am824_professional=0;
}
else
{
break;
}
i++;
}
i--;
}
else
{
print_usage();
throwError(ERR_BAD_CMD_OPTION, "Option %s not recognized", argv[i]);
}
}
if (read_wav_file_header(playbackWavFileName, &data))
{
throwError(ERR_BAD_WAV_FORMAT, "Bad wav header");
}
audio = malloc(data.totalBytes);
if (!audio)
{
throwError(ERR_NO_MEMORY, "Memory allocation failed");
}
bytesRead = read_entire_wav_file(&data, audio);
if (bytesRead != data.totalBytes)
{
throwError(ERR_INCOMPLETE_INPUT_FILE, "Couldn't read all input data");
}
if (am824Mode)
{
printf("AM824 output mode selected\n");
am824Convert(&data, audio, &am824audio);
free(audio);
audio = am824audio;
}
if (outputParameters.device == paNoDevice)
{
outputParameters.device = Pa_GetDefaultOutputDevice();
}
if (outputParameters.device == paNoDevice)
{
throwError(ERR_NO_DEVICE, "No default output device");
}
#if defined(_WIN32) || defined(_WIN64)
if ((waspiExclusiveMode) &&
(Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->type == paWASAPI)) {
wasapiInfo.size = sizeof(PaWasapiStreamInfo);
wasapiInfo.hostApiType = paWASAPI;
wasapiInfo.version = 1;
wasapiInfo.flags = (paWinWasapiExclusive | paWinWasapiThreadPriority);
wasapiInfo.channelMask = 0;
wasapiInfo.hostProcessorOutput = NULL;
wasapiInfo.hostProcessorInput = NULL;
wasapiInfo.threadPriority = eThreadPriorityProAudio;
outputParameters.hostApiSpecificStreamInfo = (&wasapiInfo);
printf("Detected WASAPI device and setting exclusive mode\n");
}
else
{
#endif
outputParameters.hostApiSpecificStreamInfo = NULL;
#if defined(_WIN32) || defined(_WIN64)
}
#endif
if (userLatency > 0.0)
{
outputParameters.suggestedLatency = userLatency;
}
else
{
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
}
switch(data.waveFileFormat)
{
case FF_PCM:
if ((data.bitsPerSample != 8) && (data.bitsPerSample != 16)
&& (data.bitsPerSample != 24) && (data.bitsPerSample != 32))
{
throwError(ERR_NOT_SUPPORTED, "Unsupported WAVE_FORMAT_PCM bitdepth");
}
break;
case FF_FLOAT32:
if (data.bitsPerSample != 32)
{
throwError(ERR_BAD_WAV_FORMAT, "Wavefile indicated floating point but bits per sample is not 32");
}
break;
default:
throwError(ERR_NOT_SUPPORTED, "Unsupported WAV format tag (WAVE_FORMAT_PCM & WAVE_FORMAT_IEEE_FLOAT supported)");
}
if (data.bitsPerSample == 16)
{
outputParameters.sampleFormat = paInt16;
}
else if (data.bitsPerSample == 8)
{
outputParameters.sampleFormat = paInt8;
}
else if (data.bitsPerSample == 24)
{
outputParameters.sampleFormat = paInt24;
}
else if (data.bitsPerSample == 32)
{
if (data.waveFileFormat == FF_FLOAT32)
{
outputParameters.sampleFormat = paFloat32;
}
else
{
outputParameters.sampleFormat = paInt32;
}
}
else
{
throwError(ERR_NOT_SUPPORTED, "Unsupported bitdepth %d", data.bitsPerSample);
}
outputParameters.channelCount = data.numChannels;
sampleRate = (double)data.fs;
err = Pa_IsFormatSupported(NULL, &outputParameters, sampleRate);
if (err != paNoError)
{
throwError(ERR_NOT_SUPPORTED, "Pa_IsFormatSupported returned %d, %s", err, Pa_GetErrorText(err));
}
printf("Pa_IsFormatSupported succeeded\n");
printf("device: %u\nchannels: %u\nsampleFormat: %lu\nlatency: %f\nsampleRate: %u\n", outputParameters.device, outputParameters.channelCount, outputParameters.sampleFormat, outputParameters.suggestedLatency, data.fs);
// Set callback parameters
data.frameIndex = 0; /* Index into sample array. */
data.maxFrameIndex = (unsigned long) data.totalBytes / data.blockAlign;
data.audio = (unsigned char *)audio;
printf("\n=== Now playing back. ===\n"); fflush(stdout);
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
data.fs,
framesPerBuffer,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
&data );
if( err != paNoError )
{
Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa_OpenStream returned %d, %s", err, Pa_GetErrorText( err ));
}
if( stream )
{
#if defined(__linux__ )
PaAlsa_EnableRealtimeScheduling( stream, 1 );
printf("RealTime Scheduling enabled\n");
#endif
err = Pa_StartStream( stream );
if( err != paNoError )
{
Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa_StartStream returned %d, %s", err, Pa_GetErrorText( err ));
}
printf("Waiting for playback to start.\n");
do
{
startTime = Pa_GetStreamTime(stream);
}
while (startTime == 0.0);
printf("Waiting for playback to finish.\n");
fflush(stdout);
finishTime = data.maxFrameIndex / data.fs;
while ((err = Pa_IsStreamActive(stream)) == 1)
{
Pa_Sleep(1000);
}
printf("\n");
if( err != paNoError )
{
Pa_Terminate();
throwError(ERR_NO_STREAM, "Pa error %d received during playback: %s", err, Pa_GetErrorText( err ));
}
err = Pa_CloseStream( stream );
if( err != paNoError )
printf("Done.\n"); fflush(stdout);
}
if (audio)
{
free(audio);
}
Pa_Terminate();
exit(0);
}

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{8471C61A-8280-468C-B94B-255BEF70289D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>wavplay_am824</RootNamespace>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalLibraryDirectories>lib\Win32\Debug</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;portaudio_x86.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalLibraryDirectories>lib\x64\Debug</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;portaudio_x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>lib\Win32\Release</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;portaudio_x86.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>lib\x64\Release</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;portaudio_x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\portaudio.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="wavplay_am824.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>