207 lines
5.6 KiB
C
207 lines
5.6 KiB
C
|
|
#define SND_MSGUNDER
|
|
|
|
typedef struct {
|
|
snd_pcm_t *handle;
|
|
void *buffer;
|
|
int *conv;
|
|
uint samplerate;
|
|
uint blocksize;
|
|
uint channels;
|
|
uint width;
|
|
uint smppos;
|
|
uint devbufsize;
|
|
#ifdef SND_MSGUNDER
|
|
uint underruns;
|
|
uint lastunder;
|
|
uint pushed;
|
|
uint pad;
|
|
#else
|
|
uint pad[4];
|
|
#endif
|
|
} aud_handle;
|
|
|
|
void aud_prepare(aud_handle *dev) {
|
|
if(dev->handle == NULL) {
|
|
return;
|
|
}
|
|
memset(dev->buffer, 0, dev->blocksize * dev->width * dev->channels);
|
|
dev->smppos = 0;
|
|
int rc = 0;
|
|
uint tries = 0;
|
|
while(rc != dev->blocksize) {
|
|
if(tries++ >= 256) {
|
|
snd_logw(STR_AUD_PREPERR);
|
|
return;
|
|
}
|
|
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
|
|
if(rc == -EAGAIN || (rc >= 0 && (uint)rc < dev->blocksize)) {
|
|
snd_pcm_wait(dev->handle, 100);
|
|
}
|
|
else if(rc == -EPIPE) {
|
|
snd_pcm_prepare(dev->handle);
|
|
}
|
|
}
|
|
snd_pcm_nonblock(dev->handle, 1);
|
|
for(int z = 0; z < 16; z++) {
|
|
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
|
|
if(rc == -EAGAIN || (rc >= 0 && (uint)rc < dev->blocksize)) {
|
|
snd_pcm_wait(dev->handle, 100);
|
|
}
|
|
else if(rc == -EPIPE) {
|
|
snd_pcm_prepare(dev->handle);
|
|
}
|
|
}
|
|
snd_pcm_nonblock(dev->handle, 0);
|
|
}
|
|
|
|
void aud_write(aud_handle *dev, void *data, uint samples) {
|
|
int rc;
|
|
uint blocksize = dev->blocksize;
|
|
while(samples > 0) {
|
|
blocksize = samples < blocksize ? samples : blocksize;
|
|
if(dev->handle) {
|
|
rc = snd_pcm_writei(dev->handle, data, blocksize);
|
|
}
|
|
data += blocksize * dev->channels;
|
|
samples -= blocksize;
|
|
if(dev->handle) {
|
|
if(rc == -EAGAIN || (rc >= 0 && (uint)rc < blocksize)) {
|
|
snd_pcm_wait(dev->handle, 100);
|
|
}
|
|
else if(rc == -EPIPE) {
|
|
#ifdef SND_MSGUNDER
|
|
if(dev->underruns == 0) {
|
|
dev->lastunder = dev->pushed;
|
|
}
|
|
dev->underruns += 1;
|
|
#endif
|
|
snd_pcm_prepare(dev->handle);
|
|
}
|
|
else if(rc < 0) {
|
|
snd_loge(STR_AUD_WRITERR, snd_strerror(rc), rc);
|
|
}
|
|
#ifdef SND_MSGUNDER
|
|
else {
|
|
dev->pushed += 1;
|
|
}
|
|
if((dev->underruns > 0) && ((dev->pushed - dev->lastunder) >= (dev->samplerate / dev->blocksize))) {
|
|
snd_logw(STR_AUD_UNDERR, dev->underruns);
|
|
dev->underruns = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
uint aud_open_dev(aud_handle *dev, const char *devname, uint samplerate, byte channels, byte width, uint frames, uint bufsize) {
|
|
dev->channels = (uint)channels;
|
|
dev->samplerate = samplerate;
|
|
dev->width = (uint)width;
|
|
dev->smppos = 0;
|
|
#ifdef SND_MSGUNDER
|
|
dev->underruns = 0;
|
|
dev->lastunder = 0;
|
|
dev->pushed = 0;
|
|
#endif
|
|
dev->handle = NULL;
|
|
dev->buffer = NULL;
|
|
dev->conv = NULL;
|
|
int rc;
|
|
int subdir = 0;
|
|
snd_pcm_hw_params_t *params = NULL;
|
|
rc = snd_pcm_open(&dev->handle, devname, SND_PCM_STREAM_PLAYBACK, 0);
|
|
if(rc < 0) {
|
|
snd_loge(STR_AUD_OPNERR, devname, snd_strerror(rc), rc);
|
|
return 0;
|
|
}
|
|
snd_pcm_hw_params_alloca(¶ms);
|
|
snd_pcm_hw_params_any(dev->handle, params);
|
|
snd_pcm_hw_params_set_access(dev->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
if(snd_pcm_hw_params_set_format(dev->handle, params, (dev->width == 4) ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S16_LE) != 0) {
|
|
dev->width = (width == 4) ? 2 : 4;
|
|
if(snd_pcm_hw_params_set_format(dev->handle, params, (dev->width == 4) ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S16_LE) != 0) {
|
|
snd_loge(STR_AUD_FMTERR, devname);
|
|
snd_pcm_close(dev->handle);
|
|
return 0;
|
|
}
|
|
else {
|
|
snd_logw(STR_AUD_FMTCH, devname, width * 8, dev->width * 8);
|
|
}
|
|
}
|
|
snd_pcm_hw_params_set_channels(dev->handle, params, dev->channels);
|
|
snd_pcm_hw_params_set_rate_near(dev->handle, params, &dev->samplerate, &subdir);
|
|
snd_pcm_uframes_t framesize = frames;
|
|
snd_pcm_hw_params_set_period_size_near(dev->handle, params, &framesize, &subdir);
|
|
snd_pcm_uframes_t buffersize = bufsize;
|
|
if(bufsize > 0) {
|
|
snd_pcm_hw_params_set_buffer_size_near(dev->handle, params, &buffersize);
|
|
}
|
|
rc = snd_pcm_hw_params(dev->handle, params);
|
|
if(rc < 0) {
|
|
snd_loge(STR_AUD_HWERR, devname, snd_strerror(rc), rc);
|
|
snd_pcm_close(dev->handle);
|
|
return 0;
|
|
}
|
|
snd_pcm_hw_params_get_period_size(params, &framesize, &subdir);
|
|
snd_pcm_hw_params_get_buffer_size(params, &buffersize);
|
|
snd_pcm_sw_params_t *swparams;
|
|
snd_pcm_sw_params_alloca(&swparams);
|
|
snd_pcm_sw_params_current(dev->handle, swparams);
|
|
// snd_pcm_sw_params_set_avail_min(dev->handle, swparams, framesize);
|
|
// snd_pcm_sw_params_set_start_threshold(dev->handle, swparams, 0);
|
|
// snd_pcm_sw_params_set_stop_threshold(dev->handle, swparams, buffersize);
|
|
rc = snd_pcm_sw_params(dev->handle, swparams);
|
|
if(rc < 0) {
|
|
snd_loge(STR_AUD_SWERR, devname, snd_strerror(rc), rc);
|
|
snd_pcm_close(dev->handle);
|
|
return 0;
|
|
}
|
|
|
|
dev->blocksize = framesize;
|
|
dev->devbufsize = buffersize;
|
|
dev->buffer = malloc(dev->blocksize * dev->width * dev->channels);
|
|
if(dev->width == 4)
|
|
dev->conv = malloc(sizeof(int) * dev->channels);
|
|
snd_pcm_wait(dev->handle, -1);
|
|
aud_prepare(dev);
|
|
return dev->samplerate;
|
|
}
|
|
|
|
void aud_push_raw(aud_handle *dev, void *data) {
|
|
memcpy(&((byte*)(dev->buffer))[dev->smppos*dev->channels*dev->width], data, dev->width * dev->channels);
|
|
dev->smppos++;
|
|
if(dev->smppos == dev->blocksize) {
|
|
aud_write(dev, dev->buffer, dev->blocksize);
|
|
dev->smppos = 0;
|
|
}
|
|
}
|
|
|
|
void aud_push(aud_handle *dev, short *data) {
|
|
if(dev->width == 4) {
|
|
for(int h = 0; h < dev->channels; h++) {
|
|
dev->conv[h] = ((int)data[h]) << 16;
|
|
}
|
|
aud_push_raw(dev, dev->conv);
|
|
}
|
|
else {
|
|
aud_push_raw(dev, data);
|
|
}
|
|
}
|
|
|
|
void aud_flush(aud_handle *dev) {
|
|
if(dev->smppos != 0) {
|
|
aud_write(dev, dev->buffer, dev->smppos);
|
|
dev->smppos = 0;
|
|
}
|
|
}
|
|
|
|
void aud_close(aud_handle *dev) {
|
|
if(dev->handle) {
|
|
snd_pcm_drain(dev->handle);
|
|
snd_pcm_close(dev->handle);
|
|
}
|
|
free(dev->buffer);
|
|
if(dev->conv)
|
|
free(dev->conv);
|
|
}
|