300 lines
7.2 KiB
C
300 lines
7.2 KiB
C
#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
|
|
rc = blocksize;
|
|
}
|
|
else {
|
|
fwrite(data, 1, blocksize * dev->channels * SND_SMPSIZE, stdout);
|
|
rc = blocksize;
|
|
}
|
|
if(rc > 0) {
|
|
data += rc * dev->channels;
|
|
samples -= rc;
|
|
}
|
|
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(¶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, 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
|