Archived
1
0
Fork 0
This repository has been archived on 2025-09-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
skcblitz/alsasnd.h

208 lines
5.6 KiB
C
Raw Permalink Normal View History

2025-09-02 14:43:36 +02:00
#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(&params);
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);
}