oplplayer/alsasnd.h

301 lines
7.2 KiB
C
Raw Permalink Normal View History

2025-03-20 11:02:57 +01:00
#ifdef SND_S32
#define pcms_t int32_t
#define aud_ihandle aud_handle32
#define SND_FMT SND_PCM_FORMAT_S32_LE
#else
#define pcms_t int16_t
#define aud_ihandle aud_handle16
#define SND_FMT SND_PCM_FORMAT_S16_LE
#endif
#define SND_SMPSIZE (sizeof(pcms_t))
typedef struct {
snd_pcm_t *handle;
uint32_t samplerate;
uint32_t blocksize;
uint32_t channels;
uint32_t smppos;
pcms_t *buffer;
uint32_t devbufsize;
wav_handle wav;
#ifdef SND_MSGUNDER
uint32_t underruns;
uint32_t lastunder;
uint32_t pushed;
#endif
} aud_ihandle;
void
#ifdef SND_S32
wav_write32
#else
wav_write16
#endif
(wav_handle *wav, pcms_t *samples, uint32_t count) {
if(wav->fd == NULL) {
return;
}
for(int c = 0; c < (count * ((uint32_t)wav->channels)); c++) {
#ifdef SND_S32
wav_write_uint32((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
#else
wav_write_uint16((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
#endif
}
if(fwrite(wav->buffer, 1, SND_SMPSIZE * ((uint32_t)wav->channels) * count, wav->fd) != (SND_SMPSIZE * ((uint32_t)wav->channels) * count)) {
fprintf(stderr, STR_WAV_EIO"\n", wav->filename);
fclose(wav->fd);
wav->fd = NULL;
return;
}
wav->samples += count;
}
void
#ifdef SND_S32
aud_prepare32
#else
aud_prepare16
#endif
(aud_ihandle *dev) {
if(dev->handle == NULL) {
return;
}
memset(dev->buffer, 0, dev->blocksize * SND_SMPSIZE * dev->channels);
dev->smppos = 0;
int rc = 0;
uint32_t tries = 0;
while(rc != dev->blocksize) {
if(tries++ >= 256) {
mid_log(STR_AUD_PREPERR);
return;
}
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
if(rc == -EAGAIN || (rc >= 0 && (size_t)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 && (size_t)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
#ifdef SND_S32
aud_write32
#else
aud_write16
#endif
(aud_ihandle *dev, pcms_t *data, uint32_t samples) {
int rc;
uint32_t blocksize = dev->blocksize;
while(samples > 0) {
blocksize = samples < blocksize ? samples : blocksize;
if(dev->handle) {
rc = snd_pcm_writei(dev->handle, data, blocksize);
}
else if(dev->wav.buffer) {
#ifdef SND_S32
wav_write32(&dev->wav, data, blocksize);
#else
wav_write16(&dev->wav, data, blocksize);
#endif
2025-03-20 12:03:01 +01:00
rc = blocksize;
2025-03-20 11:02:57 +01:00
}
else {
fwrite(data, 1, blocksize * dev->channels * SND_SMPSIZE, stdout);
2025-03-20 12:03:01 +01:00
rc = blocksize;
}
if(rc > 0) {
data += rc * dev->channels;
samples -= rc;
2025-03-20 11:02:57 +01:00
}
if(dev->handle) {
if(rc == -EAGAIN || (rc >= 0 && (size_t)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) {
mid_log(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))) {
mid_log(STR_AUD_UNDERR, dev->underruns);
dev->underruns = 0;
}
#endif
}
}
}
int
#ifdef SND_S32
aud_open_dev32
#else
aud_open_dev16
#endif
(aud_ihandle *dev, const char *devname, uint32_t samplerate, uint8_t channels, uint32_t frames, uint32_t bufsize) {
dev->channels = channels;
dev->samplerate = samplerate;
dev->smppos = 0;
#ifdef SND_MSGUNDER
dev->underruns = 0;
dev->lastunder = 0;
dev->pushed = 0;
#endif
dev->handle = NULL;
dev->buffer = NULL;
dev->wav.buffer = NULL;
if((devname[0] == '-') && (devname[1] == 0)) {
dev->blocksize = frames;
dev->devbufsize = 0;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
return 0;
}
else if((strlen(devname) >= 4) && (strcmp(devname+(strlen(devname)-4), ".wav") == 0)) {
if(wav_open(&dev->wav, devname, samplerate, channels, SND_SMPSIZE) ^ 1) {
return false;
}
dev->blocksize = frames;
dev->devbufsize = 0;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
dev->wav.buffer = malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
return 0;
}
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) {
fprintf(stderr, STR_AUD_OPNERR"\n", devname, snd_strerror(rc), rc);
return 1;
}
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, SND_FMT) != 0) {
snd_pcm_close(dev->handle);
return 2;
}
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) {
fprintf(stderr, STR_AUD_HWERR"\n", devname, snd_strerror(rc), rc);
snd_pcm_close(dev->handle);
return 1;
}
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) {
fprintf(stderr, STR_AUD_SWERR"\n", devname, snd_strerror(rc), rc);
snd_pcm_close(dev->handle);
return 1;
}
dev->blocksize = framesize;
dev->devbufsize = buffersize;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
snd_pcm_wait(dev->handle, -1);
#ifdef SND_S32
aud_prepare32(dev);
#else
aud_prepare16(dev);
#endif
return 0;
}
void
#ifdef SND_S32
aud_push32
#else
aud_push16
#endif
(aud_ihandle *dev, pcms_t *data) {
memcpy(&dev->buffer[dev->smppos*dev->channels], data, SND_SMPSIZE * dev->channels);
dev->smppos++;
if(dev->smppos == dev->blocksize) {
#ifdef SND_S32
aud_write32(dev, dev->buffer, dev->blocksize);
#else
aud_write16(dev, dev->buffer, dev->blocksize);
#endif
dev->smppos = 0;
}
}
void
#ifdef SND_S32
aud_flush32
#else
aud_flush16
#endif
(aud_ihandle *dev) {
if(dev->smppos != 0) {
#ifdef SND_S32
aud_write32(dev, dev->buffer, dev->smppos);
#else
aud_write16(dev, dev->buffer, dev->smppos);
#endif
dev->smppos = 0;
}
}
void
#ifdef SND_S32
aud_close32
#else
aud_close16
#endif
(aud_ihandle *dev) {
if(dev->handle) {
snd_pcm_drain(dev->handle);
snd_pcm_close(dev->handle);
}
if(dev->wav.buffer) {
wav_close(&dev->wav);
free(dev->wav.buffer);
}
free(dev->buffer);
}
#undef pcms_t
#undef aud_ihandle
#undef SND_FMT