簡單的播放器
// File: play.cpp
#include "alsa/asoundlib.h"
struct ChunkHEAD {
char id[4];
int32_t length;
char *stringID() {
static char str[5];
memcpy(str, id, 4);
return str;
}
bool id_is(const char *name) {
if (*name == 0) return false;
for (int i = 0; i < 4; i ++) {
if (name[i] == 0) break; // end of String
if (name[i] != id[i]) return false;
}
return true;
}
};
struct PCMtype {
int16_t pcmType, channels;
int32_t sampleRate, byteRate;
int16_t frameBytes, bitsData;
};
struct WAVstruct {
ChunkHEAD title;
char wave[4];
ChunkHEAD fmt;
PCMtype pcm;
bool is_wav() {
if (! title.id_is("RIFF")) return false;
static const char waveID[5] = "WAVE";
for (int i = 0; i < 4; i ++) { if (wave[i] != waveID[i]) return false; }
return fmt.id_is("fmt");
}
};
struct PCM_2CH {// interleave L/R
int16_t l;
int16_t r;
};
int main(void) {
FILE *fin= fopen("sample.wav", "rb");
if (fin == nullptr) return -1;
unsigned int sampleRate = 8000;
unsigned int channels = 2;
int frameBytes = sizeof(PCM_2CH);
int frameSamples = 0;
int bufSize = 1024;
WAVstruct header;
const int n = fread(&header, 1, sizeof(WAVstruct), fin);
if (n > 0 && header.is_wav()) {
sampleRate = header.pcm.sampleRate;
channels = header.pcm.channels;
frameBytes = header.pcm.frameBytes;
ChunkHEAD chunkList;
do {
if (fread(&chunkList, 1, sizeof(ChunkHEAD), fin) <= 0) break;
if (chunkList.id_is("data")) break;
fseek(fin, chunkList.length, SEEK_CUR);// ignore unknown id
} while (! feof(fin));
frameSamples = chunkList.length / frameBytes;
}
snd_pcm_t *handle;
char speaker[bufSize * frameBytes];
if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) == 0) {
if (snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, channels, sampleRate, 1, sampleRate / 4) == 0) {
while(! feof(fin)) {
const int uframes = fread(speaker, frameBytes, bufSize, fin);
if (uframes <= 0) break;
const int err = snd_pcm_writei(handle, speaker, uframes);
if (err < 0) snd_pcm_recover(handle, err, 0);// try to recover
}
}
snd_pcm_close(handle);
}
fclose(fin);
return 0;
}
編譯並執行:
g++ play.cpp -lasound && ./a.out
後記: 改用 mmap 方式播放 wav 檔, 修改主程式
int main(int argc, char **argv) {
FILE *fin= fopen(argc > 1 ? argv[1] : "sample.wav", "rb");
if (fin == nullptr) return -1;
unsigned int sampleRate = 44100;
unsigned int channels = 2;
WAVstruct header;
const int n = fread(&header, 1, sizeof(WAVstruct), fin);
if (n > 0 && header.is_wav()) {
sampleRate = header.pcm.sampleRate;
channels = header.pcm.channels;
ChunkHEAD chunkList;
do {
if (fread(&chunkList, 1, sizeof(ChunkHEAD), fin) <= 0) break;
if (chunkList.is_id("data")) break;
chunkList.length);
fseek(fin, chunkList.length, SEEK_CUR);// ignore unknown id
} while (! feof(fin));
}
snd_pcm_t *handle;
if (snd_pcm_open(&handle, "hw:1,0", SND_PCM_STREAM_PLAYBACK, 0) == 0) { // hw:1,0 , default
if (snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_MMAP_INTERLEAVED, channels, sampleRate, 1, sampleRate / 4) == 0) {
const snd_pcm_channel_area_t* areas;
snd_pcm_uframes_t offset;
snd_pcm_uframes_t uframes = 1024;// size to request, it will be updated by DMA
snd_pcm_mmap_begin(handle, &areas, &offset, &uframes);// DMA 需要的 addr, offset, uframes
const int first = areas->first / 8;
const int uBytes = areas->step / 8;
int8_t *data = (int8_t *)areas->addr + first;
printf("begin offset = %ld, uframes =%ld, first = %d, uBytes = %d bytes\n", offset, uframes, first , uBytes);
memset(data + offset * uBytes, 0, uBytes * uframes);// fill zero data
snd_pcm_mmap_commit(handle , offset , uframes);
snd_pcm_start(handle); // 開始播放
int count = 0;
while (!feof(fin)) {
snd_pcm_wait(handle, -1);
uframes = 1024;
snd_pcm_mmap_begin(handle, &areas, &offset, &uframes);
uframes = fread(data + offset * uBytes, uBytes, uframes, fin);
if (uframes <= 0) break;
snd_pcm_mmap_commit(handle, offset, uframes);
}
}
snd_pcm_close(handle);
}
fclose(fin);
return 0;
}
沒有留言:
張貼留言