status_t AudioALSALoopbackController::open(const audio_devices_t output_devices, const audio_devices_t input_device)
{
    ALOGD("+%s(), output_devices = 0x%x, input_device = 0x%x", __FUNCTION__, output_devices, input_device);
    AudioAutoTimeoutLock _l(mLock);
    AudioAutoTimeoutLock _l2(*AudioALSADriverUtility::getInstance()->getStreamSramDramLock());

    // DL loopback setting
    memset(&mConfig, 0, sizeof(mConfig));
    mConfig.channels = 2;
    mConfig.rate = 48000;
    mConfig.period_size = 1024;
    mConfig.period_count = 2;
    mConfig.format = PCM_FORMAT_S16_LE;
    mConfig.start_threshold = 0;
    mConfig.stop_threshold = 0;
    mConfig.silence_threshold = 0;
    ALOGD("+%s(), mConfig.rate=%d", __FUNCTION__,mConfig.rate);

    ASSERT(mPcmUL == NULL && mPcmDL == NULL);
    mPcmUL = pcm_open(0, 4, PCM_IN, &mConfig);
    mPcmDL = pcm_open(0, 4, PCM_OUT, &mConfig);
    ASSERT(mPcmUL != NULL && mPcmDL != NULL);
    ALOGV("%s(), mPcmUL = %p, mPcmDL = %p", __FUNCTION__, mPcmUL, mPcmDL);

    pcm_start(mPcmUL);
    pcm_start(mPcmDL);


    mHardwareResourceManager->startInputDevice(input_device);
    mHardwareResourceManager->startOutputDevice(output_devices, mConfig.rate);


    ALOGD("-%s()", __FUNCTION__);
    return NO_ERROR;
}
Пример #2
0
status_t AudioALSACaptureDataProviderBase::openPcmDriver(const unsigned int device)
{
    ALOGD("+%s(), pcm device = %d", __FUNCTION__, device);

    ASSERT(mPcm == NULL);
    mPcm = pcm_open(kAudioSoundCardIndex, device, PCM_IN, &mConfig);
    if (mPcm == NULL)
    {
        ALOGE("%s(), mPcm == NULL!!", __FUNCTION__);
    }
    else if (pcm_is_ready(mPcm) == false)
    {
        ALOGE("%s(), pcm_is_ready(%p) == false due to %s, close pcm.", __FUNCTION__, mPcm, pcm_get_error(mPcm));
        pcm_close(mPcm);
        mPcm = NULL;
    }
    else
    {
        pcm_start(mPcm);
    }

    ALOGD("-%s(), mPcm = %p", __FUNCTION__, mPcm);
    ASSERT(mPcm != NULL);
    return NO_ERROR;
}
status_t AudioALSACaptureDataProviderBTSCO::open()
{
    ALOGD("%s()", __FUNCTION__);
    ASSERT(mClientLock.tryLock() != 0); // lock by base class attach
    AudioAutoTimeoutLock _l(mEnableLock);

    ASSERT(mEnable == false);

    // config attribute (will used in client SRC/Enh/... later) // TODO(Harvey): query this
    mStreamAttributeSource.audio_format = AUDIO_FORMAT_PCM_16_BIT;
    mStreamAttributeSource.audio_channel_mask = AUDIO_CHANNEL_IN_MONO;
    mStreamAttributeSource.num_channels = android_audio_legacy::AudioSystem::popCount(mStreamAttributeSource.audio_channel_mask);
    mStreamAttributeSource.sample_rate = mWCNChipController->GetBTCurrentSamplingRateNumber();


    // pcm config
    mConfig.channels = mStreamAttributeSource.num_channels;
    mConfig.rate = mStreamAttributeSource.sample_rate;

    // Buffer size: 2048(period_size) * 1(ch) * 2(byte) * 4(period_count) = 16 kb
    mConfig.period_size = 2048;
    mConfig.period_count = 4;
    mConfig.format = PCM_FORMAT_S16_LE;

    mConfig.start_threshold = 0;
    mConfig.stop_threshold = 0;
    mConfig.silence_threshold = 0;

    ALOGD("%s(), audio_format = %d, audio_channel_mask=%x, num_channels=%d, sample_rate=%d", __FUNCTION__,
          mStreamAttributeSource.audio_format, mStreamAttributeSource.audio_channel_mask, mStreamAttributeSource.num_channels, mStreamAttributeSource.sample_rate);

    ALOGD("%s(), format = %d, channels=%d, rate=%d", __FUNCTION__,
          mConfig.format, mConfig.channels, mConfig.rate);

    OpenPCMDump(LOG_TAG);

    // enable pcm
    ASSERT(mPcm == NULL);
    mPcm = pcm_open(0, 12, PCM_IN | PCM_MONOTONIC, &mConfig);
    ASSERT(mPcm != NULL && pcm_is_ready(mPcm) == true);
    ALOGV("%s(), mPcm = %p", __FUNCTION__, mPcm);

    pcm_start(mPcm);

    // create reading thread
    mEnable = true;
    int ret = pthread_create(&hReadThread, NULL, AudioALSACaptureDataProviderBTSCO::readThread, (void *)this);
    if (ret != 0)
    {
        ALOGE("%s() create thread fail!!", __FUNCTION__);
        return UNKNOWN_ERROR;
    }

    return NO_ERROR;
}
Пример #4
0
bool SpeechANCController::StartPCMIn(char mTypePCM, uint32_t device, pcm_config mConfig)
{
    ALOGD("+%s(), pcm device = %d", __FUNCTION__, device);
    pcm *mPcm;

    //ASSERT(mPcm == NULL);
    mPcm = pcm_open(kAudioSoundCardIndex, device, PCM_IN, &mConfig);
    if (mPcm == NULL)
    {
        ALOGE("%s(), mPcm == NULL!!", __FUNCTION__);
    }
    else if (pcm_is_ready(mPcm) == false)
    {
        ALOGE("%s(), pcm_is_ready(%p) == false due to %s, close pcm.", __FUNCTION__, mPcm, pcm_get_error(mPcm));
        pcm_close(mPcm);
        mPcm = NULL;
    }
    else
    {
        pcm_start(mPcm);
    }

    ALOGD("-%s(), mPcm = %p", __FUNCTION__, mPcm);
    ASSERT(mPcm != NULL);

    switch (mTypePCM)
    {
        case 0:
            mPcmIn_MOD = mPcm ;
            break;
        case 1:
            mPcmIn_IO2 = mPcm;
            break;
        case 2:
            mPcmIn_ADC2 = mPcm;
            break;
    }

    return true;

}
status_t AudioALSACaptureDataProviderTDM::open()
{
    ALOGD("%s()", __FUNCTION__);
    ASSERT(mClientLock.tryLock() != 0); // lock by base class attach
    AudioAutoTimeoutLock _l(mEnableLock);
    AudioAutoTimeoutLock _l2(*AudioALSADriverUtility::getInstance()->getStreamSramDramLock());

    ASSERT(mEnable == false);

    // config attribute (will used in client SRC/Enh/... later) // TODO(Harvey): query this
    mStreamAttributeSource.audio_format = AUDIO_FORMAT_PCM_16_BIT;
    mStreamAttributeSource.audio_channel_mask = AUDIO_CHANNEL_IN_STEREO;
    mStreamAttributeSource.num_channels = android_audio_legacy::AudioSystem::popCount(mStreamAttributeSource.audio_channel_mask);
    mStreamAttributeSource.sample_rate = 44100;

    OpenPCMDump(LOG_TAG);

    // enable pcm
    ASSERT(mPcm == NULL);
    mPcm = pcm_open(0, 13, PCM_IN, &mConfig);
    //    mPcm = pcm_open(0, 1, PCM_IN, &mConfig);

    ASSERT(mPcm != NULL && pcm_is_ready(mPcm) == true);
    ALOGV("%s(), mPcm = %p", __FUNCTION__, mPcm);

    pcm_start(mPcm);

    // create reading thread
    mEnable = true;
    int ret = pthread_create(&hReadThread, NULL, AudioALSACaptureDataProviderTDM::readThread, (void *)this);
    if (ret != 0)
    {
        ALOGE("%s() create thread fail!!", __FUNCTION__);
        return UNKNOWN_ERROR;
    }

    return NO_ERROR;
}
void AudioALSAPlaybackHandlerNormal::OpenHpImpeDancePcm(void)
{
    ALOGD("OpenHpImpeDancePcm");
    // Don't lock Sram/Dram lock here, it would be locked by caller function

    int pcmindex = AudioALSADeviceParser::getInstance()->GetPcmIndexByString(keypcmHpimpedancePlayback);
    int cardindex = AudioALSADeviceParser::getInstance()->GetCardIndexByString(keypcmHpimpedancePlayback);
    struct pcm_params *params;
    params = pcm_params_get(cardindex, pcmindex,  PCM_OUT);
    if (params == NULL)
    {
        ALOGD("Device does not exist.\n");
    }
    int buffer_size = pcm_params_get_max(params, PCM_PARAM_BUFFER_BYTES);
    ALOGD("buffer_size = %d", buffer_size);
    pcm_params_free(params);
    // HW pcm config
    mHpImpedanceConfig.channels = 2;
    mHpImpedanceConfig.rate = mStreamAttributeSource->sample_rate;
    mHpImpedanceConfig.period_count = 2;
    mHpImpedanceConfig.period_size = (buffer_size / (mHpImpedanceConfig.channels * mHpImpedanceConfig.period_count)) / 2;
    mHpImpedanceConfig.format = PCM_FORMAT_S16_LE;
    mHpImpedanceConfig.start_threshold = 0;
    mHpImpedanceConfig.stop_threshold = 0;
    mHpImpedanceConfig.silence_threshold = 0;
    ALOGD("%s(), mHpImpedanceConfig: channels = %d, rate = %d, period_size = %d, period_count = %d, format = %d",
          __FUNCTION__, mHpImpedanceConfig.channels, mHpImpedanceConfig.rate, mHpImpedanceConfig.period_size, mHpImpedanceConfig.period_count, mHpImpedanceConfig.format);
    // open pcm driver
    mHpImpeDancePcm = pcm_open(cardindex, pcmindex, PCM_OUT, &mHpImpedanceConfig);
    if (mHpImpeDancePcm == NULL)
    {
        ALOGE("%s(), mPcm == NULL!!", __FUNCTION__);
    }
    ALOGD("-%s(), mPcm = %p", __FUNCTION__, mHpImpeDancePcm);
    ASSERT(mHpImpeDancePcm != NULL);
    pcm_start(mHpImpeDancePcm);
}
Пример #7
0
status_t AudioALSAFMController::setFmDirectConnection(const bool enable, const bool bforce)
{
    ALOGD("+%s(), enable = %d, bforce = %d", __FUNCTION__, enable, bforce);

    // Check Current Status
    if (mIsFmDirectConnectionMode == enable && bforce == false)
    {
        ALOGW("-%s(), enable = %d, bforce = %d", __FUNCTION__, enable, bforce);
        return INVALID_OPERATION;
    }


    // Apply
    if (enable == true)
    {
        mConfig.channels = 2;
        mConfig.rate = getFmDownlinkSamplingRate();
        mConfig.period_size = 3072;
        mConfig.period_count = 2;
        mConfig.format = PCM_FORMAT_S16_LE;
        mConfig.start_threshold = 0;
        mConfig.stop_threshold = 0;
        mConfig.silence_threshold = 0;

        if (mPcm == NULL)
        {
            // Get pcm open Info
            int card_index = -1;
            int pcm_index = -1;
            if (WCNChipController::GetInstance()->IsFMMergeInterfaceSupported() == true)
            {
                card_index = AudioALSADeviceParser::getInstance()->GetCardIndexByString(keypcmMRGrxPlayback);
                pcm_index = AudioALSADeviceParser::getInstance()->GetPcmIndexByString(keypcmMRGrxPlayback);
            }
            else
            {
                card_index = AudioALSADeviceParser::getInstance()->GetCardIndexByString(keypcmFMI2SPlayback);
                pcm_index = AudioALSADeviceParser::getInstance()->GetPcmIndexByString(keypcmFMI2SPlayback);
            }
            ALOGD("%s(), card_index = %d, pcm_index = %d", __FUNCTION__, card_index, pcm_index);

            mPcm = pcm_open(card_index, pcm_index , PCM_OUT, &mConfig);
            ALOGD("%s(), pcm_open mPcm = %p", __FUNCTION__, mPcm);
        }
        if (mPcm == NULL || pcm_is_ready(mPcm) == false)
        {
            ALOGE("%s(), Unable to open mPcm device %u (%s)", __FUNCTION__, 6 , pcm_get_error(mPcm));
        }
        pcm_start(mPcm);
    }
    else
    {
        if (mPcm != NULL)
        {
            pcm_stop(mPcm);
            pcm_close(mPcm);
            mPcm = NULL;
        }
    }


    // Update Direct Mode Status
    mIsFmDirectConnectionMode = enable;

    // Update (HW_GAIN2) Volume for Direct Mode Only
    if (mIsFmDirectConnectionMode == true)
    {
        setFmVolume(mFmVolume);
    }


    ALOGD("-%s(), enable = %d, bforce = %d", __FUNCTION__, enable, bforce);
    return NO_ERROR;
}
static int stream_transfer(struct stream_transfer *stream_transfer)
{

	struct dev_stream *stream_sender;
	struct dev_stream *stream_receiver;
	short* Srcptr;
	short* Drcptr;
	int size_transfer = 0;
	int ret   =0;
	int exit_flag   =0;
    	int i=0;

	stream_sender = stream_transfer->stream_sender;
	stream_receiver = stream_transfer->stream_receiver;
	size_transfer = stream_sender->buf_size;


#ifdef  START_ZERO_BUFFER
	/* 消除开头杂音 */
	memset(stream_sender->buf, 0, stream_sender->buf_size);
	pcm_write(stream_receiver->dev, stream_sender->buf, stream_sender->buf_size);
#endif


	ret =pcm_wait(stream_sender->dev, 0);
	ret =pcm_wait(stream_receiver->dev, 0);

	pcm_stop(stream_receiver->dev);
	pcm_start(stream_receiver->dev);


	/* 消除开头pa音 */
	memset(stream_sender->buf, 0, stream_sender->buf_size);
	pcm_write(stream_receiver->dev, stream_sender->buf, stream_sender->buf_size);


	while( 1 ){

		if ( (!stream_transfer->voice_thread_run_flag)){
			break;	
		}
#if 0
		if (SNDRV_PCM_STATE_XRUN == get_pcm_state(stream_sender->dev) ){
			//ALOGD("read  SNDRV_PCM_STATE_XRUN ");
			if(ioctl(stream_sender->dev->fd, SNDRV_PCM_IOCTL_PREPARE)){
                		ALOGE("in read, fail to prepare SNDRV_PCM_STATE_XRUN ");
			}
			//usleep(3 * 1000);
			ret =pcm_wait(stream_sender->dev, 0);
			//ALOGD("pcm_read, pcm_wait ret=%d", ret);

			//ALOGD("read after prepare state:%d ",get_pcm_state(stream_sender->dev));
		}
#endif
		ret = pcm_read(stream_sender->dev, stream_sender->buf, size_transfer);
		if (ret != 0) {
			//exit_flag = 1;
			ALOGE("err: read codec err:%s, ret=%d", strerror(errno), ret);
			//break;
		}



		if ( (!stream_transfer->voice_thread_run_flag)){
			break;	
		}
#if 0
		if (SNDRV_PCM_STATE_XRUN == get_pcm_state(stream_receiver->dev) ){
			//ALOGD("write  SNDRV_PCM_STATE_XRUN ");
			pcm_stop(stream_receiver->dev);
			usleep(3 * 1000);
			//ALOGD("write after stop state:%d ",get_pcm_state(stream_receiver->dev));
			pcm_start(stream_receiver->dev);


			ret =pcm_wait(stream_receiver->dev, 0);
			//ALOGD("pcm_write, pcm_wait ret=%d", ret);

			//usleep(3 * 1000);
			//ALOGD("write after prepare state:%d ",get_pcm_state(stream_receiver->dev));
		}
#endif
		ret = pcm_write(stream_receiver->dev, stream_sender->buf, size_transfer);
		if (ret != 0) {
			//exit_flag = 1;
			ALOGE("err: write pcm err:%s, ret=%d", strerror(errno), ret);
		}

		if ( (!stream_transfer->voice_thread_run_flag)){
			break;	
		}



		if (stream_transfer->record_flag == 1){
			//是上行,还是下行.
			if (stream_transfer->voice_direction == UPSTREAM){
				Srcptr = (short*)(stream_sender->buf);
				Drcptr = (short*)(record_data.record_buf + (record_data.lenwriteup%record_data.record_lenth));
				if(record_data.lenwriteup >= record_data.lenwritedown)
				{
					memcpy(Drcptr,Srcptr,size_transfer);
				}
				else
				{
					int i;
					for(i=0;i<size_transfer/2;i++,Drcptr++)
					{
						*Drcptr = (*Drcptr + *Srcptr++)/2;
					}
					record_data.lenwrite += size_transfer;

				}
				record_data.lenwriteup += size_transfer;
				//ALOGD("stream is upload");
			} else {
				Srcptr = (short*)(stream_sender->buf);
				Drcptr = (short*)(record_data.record_buf + (record_data.lenwritedown%record_data.record_lenth));
				if(record_data.lenwritedown >= record_data.lenwriteup)
				{
					memcpy(Drcptr,Srcptr,size_transfer);
				}
				else
				{

					for(i=0;i<size_transfer/2;i++,Drcptr++)
					{
						*Drcptr = ((int)*Drcptr + (int)(*Srcptr++))/2;
					}
					record_data.lenwrite += size_transfer;
				}
				record_data.lenwritedown += size_transfer;
			}	
			sem_post(&g_sem_record);
		}

		if ( (!stream_transfer->voice_thread_run_flag)){
			break;	
		}
	}

	return 0;
}
Пример #9
0
status_t AudioALSACaptureDataProviderEchoRefExt::open()
{
    ALOGD("%s()", __FUNCTION__);
    ASSERT(mClientLock.tryLock() != 0); // lock by base class attach
    AudioAutoTimeoutLock _l(mEnableLock);
    AudioAutoTimeoutLock _l2(*AudioALSADriverUtility::getInstance()->getStreamSramDramLock());

    ASSERT(mEnable == false);

    AudioALSASampleRateController *pAudioALSASampleRateController = AudioALSASampleRateController::getInstance();
    pAudioALSASampleRateController->setScenarioStatus(PLAYBACK_SCENARIO_ECHO_REF_EXT);

    // config attribute (will used in client SRC/Enh/... later) // TODO(Sam): query the mConfig?
    mStreamAttributeSource.audio_format = AUDIO_FORMAT_PCM_16_BIT;
    mStreamAttributeSource.audio_channel_mask = AUDIO_CHANNEL_IN_STEREO;
    mStreamAttributeSource.num_channels = android_audio_legacy::AudioSystem::popCount(mStreamAttributeSource.audio_channel_mask);
    mStreamAttributeSource.sample_rate = AudioALSASampleRateController::getInstance()->getPrimaryStreamOutSampleRate();


    mConfig.channels = mStreamAttributeSource.num_channels;
    mConfig.rate = mStreamAttributeSource.sample_rate;

    // Buffer size: 2048(period_size) * 2(ch) * 2(byte) * 8(period_count) = 64 kb
    mConfig.period_size = 2048;
    mConfig.period_count = 8;
    mConfig.format = PCM_FORMAT_S16_LE;

    mConfig.start_threshold = 0;
    mConfig.stop_threshold = 0;
    mConfig.silence_threshold = 0;


#if 0
    //latency time, set as DataProvider buffer size
    mStreamAttributeSource.latency = (kReadBufferSize * 1000) / (mStreamAttributeSource.num_channels * mStreamAttributeSource.sample_rate *
                                                                 (mStreamAttributeSource.audio_format == AUDIO_FORMAT_PCM_8_BIT ? 1 :    //8  1byte/frame
                                                                  (mStreamAttributeSource.audio_format == AUDIO_FORMAT_PCM_32_BIT ? 4 :   //24bit 3bytes/frame
                                                                   2)));   //default 2bytes/sample
#else
    //latency time, set as hardware buffer size
    mStreamAttributeSource.latency = (mConfig.period_size * mConfig.period_count * 1000) / mConfig.rate;
#endif

    ALOGD("%s(), audio_format = %d, audio_channel_mask=%x, num_channels=%d, sample_rate=%d, latency=%dms", __FUNCTION__,
          mStreamAttributeSource.audio_format, mStreamAttributeSource.audio_channel_mask, mStreamAttributeSource.num_channels, mStreamAttributeSource.sample_rate, mStreamAttributeSource.latency);

    ALOGD("%s(), format = %d, channels=%d, rate=%d", __FUNCTION__,
          mConfig.format, mConfig.channels, mConfig.rate);


#ifdef NXP_SMARTPA_SUPPORT
    MTK_Tfa98xx_EchoReferenceConfigure(1);
#endif

    OpenPCMDump(LOG_TAG);

    // enable pcm
    ASSERT(mPcm == NULL);
    mPcm = pcm_open(0, 16, PCM_IN | PCM_MONOTONIC, &mConfig);
    ASSERT(mPcm != NULL && pcm_is_ready(mPcm) == true);

    pcm_start(mPcm);

    // create reading thread
    mEnable = true;
    int ret = pthread_create(&hReadThread, NULL, AudioALSACaptureDataProviderEchoRefExt::readThread, (void *)this);
    if (ret != 0)
    {
        ALOGE("%s() create thread fail!!", __FUNCTION__);
        return UNKNOWN_ERROR;
    }

    return NO_ERROR;
}
Пример #10
0
int main(int ac, char** av)
{
  pcm_desc_t desc;
  pcm_handle_t ipcm;
  pcm_handle_t opcm;
  mod_handle_t mod;
  int err;
  cmdline_t cmd;
  size_t i;

  err = -1;

  if (get_cmdline(&cmd, ac - 1, av + 1)) goto on_error_0;

  pcm_init_desc(&desc);
  desc.flags |= PCM_FLAG_IN;
  if (cmd.flags & CMDLINE_FLAG(IPCM)) desc.name = cmd.ipcm;
  if (pcm_open(&ipcm, &desc)) goto on_error_0;

  pcm_init_desc(&desc);
  desc.flags |= PCM_FLAG_OUT;
  if (cmd.flags & CMDLINE_FLAG(OPCM)) desc.name = cmd.opcm;
  if (pcm_open(&opcm, &desc)) goto on_error_1;

  if (mod_open(&mod, 512)) goto on_error_2;

  if (pcm_start(&ipcm)) goto on_error_3;
  if (pcm_start(&opcm)) goto on_error_3;

  signal(SIGINT, on_sigint);

  for (i = 0; is_sigint == 0; i += 1)
  {
    size_t nsampl;
    size_t navail;
    size_t off;

    /* read ipcm */

    err = snd_pcm_wait(ipcm.pcm, -1);
    if (is_sigint) break ;
    if (err < 0) goto on_ipcm_xrun;

    navail = (size_t)snd_pcm_avail_update(ipcm.pcm);

    if (ipcm.wpos >= ipcm.rpos) nsampl = ipcm.nsampl - ipcm.wpos;
    else nsampl = ipcm.rpos - ipcm.wpos;
    if (nsampl > navail) nsampl = navail;

    off = ipcm.wpos * ipcm.scale;
    err = snd_pcm_readi(ipcm.pcm, ipcm.buf + off, nsampl);
    if (err < 0) goto on_ipcm_xrun;

    ipcm.wpos += (size_t)err;
    if (ipcm.wpos == ipcm.nsampl) ipcm.wpos = 0;

    /* apply modifier */

  redo_mod:
    if (ipcm.wpos >= ipcm.rpos) nsampl = ipcm.wpos - ipcm.rpos;
    else nsampl = ipcm.nsampl - ipcm.rpos + ipcm.wpos;

    if (cmd.flags & CMDLINE_FLAG(FILT))
    {
      const size_t n = mod_apply
	(&mod, ipcm.buf, ipcm.nsampl, ipcm.rpos, nsampl);
      nsampl = n;
    }

    if (nsampl == 0) continue ;

    if ((ipcm.rpos + nsampl) > ipcm.nsampl)
    {
      const size_t n = ipcm.nsampl - ipcm.rpos;
      off = ipcm.rpos * ipcm.scale;
      err = snd_pcm_writei(opcm.pcm, ipcm.buf + off, n);
      if (err < 0) goto on_opcm_xrun;
      nsampl -= n;
      ipcm.rpos = 0;
    }

    off = ipcm.rpos * ipcm.scale;
    err = snd_pcm_writei(opcm.pcm, ipcm.buf + off, nsampl);
    if (err < 0) goto on_opcm_xrun;
    ipcm.rpos += nsampl;
    if (ipcm.rpos == ipcm.nsampl) ipcm.rpos = 0;

    goto redo_mod;

    continue ;

  on_ipcm_xrun:
    if (pcm_recover_xrun(&ipcm, err)) PERROR_GOTO("", on_error_2);
    continue ;

  on_opcm_xrun:
    if (pcm_recover_xrun(&opcm, err)) PERROR_GOTO("", on_error_2);
    continue ;
  }

  err = 0;

 on_error_3:
  mod_close(&mod);
 on_error_2:
  pcm_close(&opcm);
 on_error_1:
  pcm_close(&ipcm);
 on_error_0:
  return err;
}