#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 } else { fwrite(data, 1, blocksize * dev->channels * SND_SMPSIZE, stdout); } data += blocksize * dev->channels; samples -= blocksize; 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