Der thread ist zwar schon ein bischen älter, aber ich habe den WAV Player mal ein bischen in Ordnung gebracht:
#include <alsa/asoundlib.h>
#include <bitset>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
/**
* The canonical WAVE format starts with the RIFF header
*/
typedef struct
{
// Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form).
// The default byte ordering assumed for WAVE data files is little-endian.
// Files written using the big-endian byte ordering scheme have the identifier RIFX instead of RIFF.
unsigned char id[4];
/*
36 + SubChunk2Size, or more precisely:
4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
This is the size of the rest of the chunk
following this number. This is the size of the
entire file in bytes minus 8 bytes for the
two fields not included in this count:
ChunkID and ChunkSize.
*/
uint32_t length;
// Contains the letters "WAVE" (0x57415645 big-endian form).
unsigned char type[4];
} Header;
/**
* The "WAVE" format consists of two subchunks: "fmt " and "data":
* The "fmt " subchunk describes the sound data's format
*/
typedef struct
{
// Contains the letters "fmt " (0x666d7420 big-endian form).
unsigned char id[4];
// 16 for PCM
uint32_t length;
// PCM = 1 (i.e. Linear quantization)
// Values other than 1 indicate some
// form of compression.
uint16_t fmt;
// Mono = 1, Stereo = 2, etc.
uint16_t channels;
// 8000, 44100, etc.
uint32_t samplesPerSec;
// ByteRate == SampleRate * NumChannels * BitsPerSample/8
uint32_t avgBytesPerSec;
// BlockAlign == NumChannels * BitsPerSample/8
// The number of bytes for one sample including all channels.
// I wonder what happens when this number isn't an integer?
uint16_t blockAlign;
// 8 bits = 8, 16 bits = 16, etc.
uint16_t bitsPerSample;
} Format;
#define WAVE_FORMAT_IEEE_FLOAT 3
/**
* The "data" subchunk contains the size of the data and the actual sound
*/
typedef struct
{
// Contains the letters "data" (0x64617461 big-endian form).
unsigned char id[4];
// Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
// This is the number of bytes in the data.
// You can also think of this as the size
// of the read of the subchunk following this number.
uint32_t length;
} ChunkHead;
class WavePlayer
{
private:
snd_pcm_t* mAudioDevice;
uint32_t mDataSize;
char* mData;
uint16_t mChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;
uint32_t mFrameCount;
string mAudioDeviceName;
string mWaveFileName;
bool openDevice ();
bool setHwParams (uint32_t channels, uint32_t sampleRate, uint16_t bitsPerSample);
public:
WavePlayer (string audioDeviceName);
~WavePlayer();
bool isDeviceOpen ();
bool loadFile (string waveFileName);
bool play ();
};
WavePlayer::WavePlayer (string audioDeviceName)
: mAudioDevice (nullptr)
, mData (nullptr)
, mChannels (0)
, mSampleRate (0)
, mBitsPerSample (0)
, mFrameCount (0)
, mAudioDeviceName ()
, mWaveFileName ()
{
if (audioDeviceName.length() == 0)
mAudioDeviceName = "plughw:1,0";
else
mAudioDeviceName = audioDeviceName;
openDevice ();
}
WavePlayer::~WavePlayer()
{
snd_pcm_drain (mAudioDevice);
if (mAudioDevice != nullptr)
snd_pcm_close (mAudioDevice);
if (mData != nullptr)
free (mData);
}
bool WavePlayer:penDevice ()
{
int err;
err = snd_pcm_open(&mAudioDevice, mAudioDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
{
cout << "Cannot open audio device " << mAudioDeviceName << " (" << snd_strerror(err) << ")" << endl;
mAudioDevice = nullptr;
return false;
}
return true;
}
bool WavePlayer::isDeviceOpen ()
{
return mAudioDevice != nullptr;
}
bool WavePlayer::setHwParams (uint32_t channels, uint32_t sampleRate, uint16_t bitsPerSample)
{
snd_pcm_format_t bits;
uint32_t resample = 1;
int err;
snd_pcm_state_t state = snd_pcm_state (mAudioDevice);
//cout << "setHwParams: PCM state " << to_string(state) << endl;
cout << "setHwParams: channels: " << to_string(channels) << " sample rate: " << to_string(sampleRate) << " bits: " << to_string(bitsPerSample) << endl;
if (channels == mChannels && sampleRate == mSampleRate && bitsPerSample == mBitsPerSample)
{
if ((err = snd_pcm_prepare (mAudioDevice)) < 0)
{
cout << "setHwParams: Error preparing audio device (" << snd_strerror(err) << ")" << endl;
return false;
}
return true;
}
mChannels = channels;
mSampleRate = sampleRate;
mBitsPerSample = bitsPerSample;
switch (mBitsPerSample)
{
case 8:
bits = SND_PCM_FORMAT_U8;
break;
case 16:
bits = SND_PCM_FORMAT_S16;
break;
case 24:
bits = SND_PCM_FORMAT_S24;
break;
case 32:
bits = SND_PCM_FORMAT_S32;
break;
}
if (snd_pcm_hw_free (mAudioDevice) < 0)
{
/*
snd_pcm_drop (mAudioDevice);
snd_pcm_wait (mAudioDevice, 3000);
state = snd_pcm_state (mAudioDevice);
cout << "setHwParams: PCM state is " << to_string(state) << " after calling snd_pcm_drop" << endl;
*/
snd_pcm_drain (mAudioDevice);
snd_pcm_close (mAudioDevice);
openDevice ();
}
snd_pcm_hw_params_t *hwParams;
if ((err = snd_pcm_hw_params_malloc(&hwParams)) < 0)
{
cout << "setHwParams: Error initilizing parameter (" << snd_strerror(err) << ")" << endl;
return false;
}
if ((err = snd_pcm_hw_params_any(mAudioDevice, hwParams)) < 0)
{
cout << "setHwParams: Error getting parameter (" << snd_strerror(err) << ")" << endl;
goto error;
}
if ((err = snd_pcm_hw_params_set_rate_resample(mAudioDevice, hwParams, resample)) < 0)
{
cout << "setHwParams: Error setting re-sampling " << snd_strerror(err) << endl;
goto error;
}
if ((err = snd_pcm_hw_params_set_access(mAudioDevice, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
cout << "setHwParams: Error setting access type (" << snd_strerror(err) << ")" << endl;
goto error;
}
if ((err = snd_pcm_hw_params_set_format(mAudioDevice, hwParams, bits)) < 0)
{
cout << "setHwParams: Error setting sample format (" << snd_strerror(err) << ")" << endl;
goto error;
}
if ((err = snd_pcm_hw_params_set_channels(mAudioDevice, hwParams, channels)) < 0)
{
cout << "setHwParams: Error setting channel count (" << snd_strerror(err) << ")" << endl;
goto error;
}
if ((err = snd_pcm_hw_params_set_rate_near(mAudioDevice, hwParams, &sampleRate, 0)) < 0)
{
cout << "setHwParams: Error setting sample rate " << to_string (sampleRate) << " (" << snd_strerror(err) << ")" << endl;
goto error;
}
if ((err = snd_pcm_hw_params(mAudioDevice, hwParams)) < 0)
{
state = snd_pcm_state (mAudioDevice);
cout << "setHwParams: Error setting params (" << snd_strerror(err) << ") PCM state " << to_string(state) << endl;
goto error;
}
// snd_pcm_hw_params_get_buffer_size(hwParams, &bufferSize);
snd_pcm_hw_params_free (hwParams);
return true;
error:
snd_pcm_hw_params_free (hwParams);
return false;
}
void dumpDataHex (char* p, size_t size)
{
printf ("\n");
for (int i = 0; i < size; i++)
{
printf ("%02X ", *(p+i));
if ((i+1) % 16 == 0)
printf ("\n");
}
printf ("\n");
}
bool WavePlayer::loadFile (string waveFileName)
{
Header head;
Format format;
ChunkHead dataHead;
cout << endl;
cout << waveFileName << endl;
int file;
file = open (waveFileName.c_str(), O_RDONLY);
if (file < 0)
return false;
char text[5];
text [4] = 0;
string fmt;
read (file, &head, sizeof(Header));
read (file, &format, sizeof(Format));
if (format.length > sizeof (format)-
{
char b;
for (int i = 0; i < format.length - (sizeof (format)-; i++)
{
read (file, &b, 1);
}
}
int i = 0;
do
{
read (file, &dataHead, sizeof(ChunkHead));
//dumpDataHex ((char*)&dataHead, sizeof(ChunkHead));
memcpy (text, dataHead.id, 4);
fmt = text;
if (fmt == "data")
break;
i++;
lseek (file, -sizeof(ChunkHead)+1, SEEK_CUR);
} while (i < 100);
if (fmt != "data")
{
cout << "loadFile: Do not find start of data chunk" << endl;
return false;
}
#if 0
memcpy (text, head.id, 4);
fmt = text;
cout << fmt;
cout << " length: " << to_string(head.length);
memcpy (text, head.type, 4);
fmt = text;
cout << " " << fmt;
memcpy (text, format.id, 4);
fmt = text;
cout << " " << fmt;
cout << " length: " << to_string(format.length);
memcpy (text, dataHead.id, 4);
fmt = text;
cout << " " << fmt;
cout << " length: " << to_string(dataHead.length) << endl;
cout << " channels: " << to_string(format.channels) << endl;
cout << " samplesPerSec: " << to_string(format.samplesPerSec) << endl;
cout << " avgBytesPerSec: " << to_string(format.avgBytesPerSec) << endl;
cout << " blockAlign: " << to_string(format.blockAlign) << endl;
cout << " bitsPerSample: " << to_string(format.bitsPerSample) << endl;
#endif
if (mData != nullptr)
{
cout << "Wait for " << mWaveFileName << "..." << endl;
snd_pcm_drain (mAudioDevice);
cout << "Go for " << waveFileName << endl;
free (mData);
}
mDataSize = dataHead.length;
mData = (char *)malloc(mDataSize);
read (file, mData, mDataSize);
mFrameCount = head.length * 8 / ((uint32_t)format.bitsPerSample * (uint32_t)format.channels);
close(file);
mWaveFileName = waveFileName;
if (format.bitsPerSample == 32)
{
if (format.fmt == WAVE_FORMAT_IEEE_FLOAT)
cout << "Audio format is 32Bit IEEE float" << endl;
if (mDataSize % 4 != 0)
cout << "loadFile: Buffer size is not multiple of 4" << endl;
char* wave16 = (char *)malloc (mDataSize/2);
float min = 0.0f;
float max = 0.0f;
for (int i = 0; i < mDataSize; i += 4)
{
float f = *((float*)(mData+i));
if (f > max)
max = f;
if (f < min)
min = f;
}
float scaling = 1.0f;
if (max > scaling)
scaling = max;
if (min < -scaling)
scaling = -min;
//scaling *= 2.0f;
cout << "loadFile: 32bit float data are in between " << to_string(min) << " and " << to_string(max) << " => Use scaling of " << to_string(scaling) << endl;
//dumpDataHex (mWave, 64);
int16_t sample16;
int16_t* dest = (int16_t*)wave16;
for (int i = 0; i < mDataSize; i += 4)
{
float f = *((float*)(mData+i));
sample16 = (int16_t)((f / scaling) * 32767);
//sample16 = (int16_t)(*p);
*dest = sample16;
dest++;
}
free (mData);
mData = wave16;
mDataSize /= 2;
format.bitsPerSample = 16;
#if 0
{
int16_t sample16;
int16_t min = 0;
int16_t max = 0;
//dumpDataHex (mWave, 64);
for (int i = 0; i < mDataSize; i += 2)
{
sample16 = *((int16_t*)(mData+i));
if (sample16 > max)
max = sample16;
if (sample16 < min)
min = sample16;
}
cout << "loadFile: 16bit signed data are in between " << to_string(min) << " and " << to_string(max) << endl;
}
#endif
}
if (!setHwParams (format.channels, format.samplesPerSec, format.bitsPerSample))
{
free (mData);
mData = nullptr;
mWaveFileName = "";
return false;
}
return true;
}
bool WavePlayer:lay ()
{
snd_pcm_uframes_t count, frames;
cout << "Play " << mWaveFileName << " - " << to_string(mFrameCount) << " frames" << endl;
snd_pcm_state_t state = snd_pcm_state (mAudioDevice);
//cout << "PCM state " << to_string(state) << endl;
if (state == SND_PCM_STATE_XRUN)
{
int err;
if ((err = snd_pcm_prepare (mAudioDevice)) < 0)
{
cout << "Play: Error preparing audio device (" << snd_strerror(err) << ")" << endl;
return false;
}
}
count = 0;
do
{
frames = snd_pcm_writei (mAudioDevice, mData + count, mFrameCount - count);
if (frames < 0)
frames = snd_pcm_recover (mAudioDevice, frames, 0);
if (frames < 0)
{
cout << "Error playing " << mWaveFileName << " (" << snd_strerror(frames) << ")" << endl;
return false;
}
count += frames;
} while (count < mFrameCount);
return true;
}
int main(void)
{
{
string fName = "16BitMono.wav";
//fName = "/usr/share/sounds/alsa/Rear_Left.wav";
WavePlayer player ("hw:1,0");
if (!player.isDeviceOpen ())
cout << "Error opening audio device" << endl;
else if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
}
#if 1
fName = "32BitMono.wav";
//fName = "/usr/share/sounds/alsa/Rear_Left.wav";
if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
}
#endif
}
#if 1
{
string fName = "/usr/share/sounds/alsa/Rear_Left.wav";
WavePlayer player ("hw:1,0");
if (!player.isDeviceOpen ())
cout << "Error opening audio device" << endl;
else if (!player.loadFile (fName))
cout << "Error loading file " << fName << endl;
else
{
for (int i = 0; i < 2; i++)
{
if (!player.play ())
{
cout << "Error playing sound" << fName << endl;
break;
}
sleep (3);
}
}
}
#endif
}
Lesezeichen