bool QAudioOutputPrivate::open() { if(opened) return true; #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif timeStamp.restart(); elapsedTimeOffset = 0; int dir; int err = 0; int count=0; unsigned int freakuency=settings.frequency(); if (!settings.isValid()) { qWarning("QAudioOutput: open error, invalid format."); } else if (settings.sampleRate() <= 0) { qWarning("QAudioOutput: open error, invalid sample rate (%d).", settings.sampleRate()); } else { err = -1; } if (err == 0) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit errorChanged(errorState); return false; } QString dev = QString(QLatin1String(m_device.constData())); QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) if (devices.size() > 0) dev = QLatin1String(devices.first()); else return false; #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(m_device); #else int idx = 0; char *name; QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); while(snd_card_get_name(idx,&name) == 0) { if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } // Step 1: try and open the device while((count < 5) && (err < 0)) { err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); if(err < 0) count++; } if (( err < 0)||(handle == 0)) { errorState = QAudio::OpenError; emit errorChanged(errorState); deviceState = QAudio::StoppedState; return false; } snd_pcm_nonblock( handle, 0 ); // Step 2: Set the desired HW parameters. snd_pcm_hw_params_alloca( &hwparams ); bool fatal = false; QString errMessage; unsigned int chunks = 8; err = snd_pcm_hw_params_any( handle, hwparams ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err); } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_access( handle, hwparams, access ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err); } } if ( !fatal ) { err = setFormat(); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); } } if ( !fatal ) { unsigned int maxBufferTime = 0; unsigned int minBufferTime = 0; unsigned int maxPeriodTime = 0; unsigned int minPeriodTime = 0; err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir); if ( err >= 0) err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir); if ( err >= 0) err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir); if ( err >= 0) err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err); } else { if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) { #ifdef DEBUG_AUDIO qDebug()<<"defaults out of range"; qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime; #endif period_time = minPeriodTime; if (period_time*4 <= maxBufferTime) { // Use 4 periods if possible buffer_time = period_time*4; chunks = 4; } else if (period_time*2 <= maxBufferTime) { // Use 2 periods if possible buffer_time = period_time*2; chunks = 2; } else { qWarning()<<"QAudioOutput: alsa only supports single period!"; fatal = true; } #ifdef DEBUG_AUDIO qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time; #endif } } } if ( !fatal ) { err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err); } } if( err < 0) { qWarning()<<errMessage; errorState = QAudio::OpenError; emit errorChanged(errorState); deviceState = QAudio::StoppedState; return false; } snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); period_size = snd_pcm_frames_to_bytes(handle,period_frames); snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); // Step 3: Set the desired SW parameters. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); snd_pcm_sw_params(handle, swparams); // Step 4: Prepare audio if(audioBuffer == 0) audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; snd_pcm_prepare( handle ); snd_pcm_start(handle); // Step 5: Setup callback and timer fallback snd_async_add_pcm_handler(&ahandler, handle, async_callback, this); bytesAvailable = bytesFree(); // Step 6: Start audio processing timer->start(period_time/1000); clockStamp.restart(); timeStamp.restart(); elapsedTimeOffset = 0; errorState = QAudio::NoError; totalTimeValue = 0; opened = true; return true; }
TErrors SalsaStream::connect( llaOutputStream* output, llaAudioPipe& buffer) { TErrors ret = _open(SND_PCM_NONBLOCK); // Update pcm settings if( ret != E_OK || (ret = updateSettings(buffer)) != E_OK ) return ret; if( (ret = output->open()) != E_OK ) { close(); return ret; } int err = 0; snd_pcm_uframes_t avail_min = 0; if(buffer.getBufferLength() < period_size_) { close(); output->close(); LOGGER().error(E_STREAM_CONFIG, "Buffer length must be higher"); return E_STREAM_CONFIG; } else if(buffer.getBufferLength() / period_size_ < 3 ) avail_min = period_size_; else avail_min = ((buffer.getBufferLength()/period_size_)-2)*period_size_; // Set up the input stream for polling ///////////////////////////////////// // This is the same process as for the output. err = snd_pcm_sw_params_malloc(&sw_config_); err = snd_pcm_sw_params_current(pcm_, sw_config_); CHECK_SNDERROR(err, E_STREAM_CONFIG); // err = snd_pcm_sw_params_set_start_threshold (pcm_, sw_config_, 0U ); // CHECK_SNDERROR(err, E_STREAM_CONFIG); err = snd_pcm_sw_params_set_avail_min(pcm_, sw_config_, avail_min ); CHECK_SNDERROR(err, E_STREAM_CONFIG); err = snd_pcm_sw_params(pcm_, sw_config_); CHECK_SNDERROR(err, E_STREAM_CONFIG); snd_pcm_prepare(pcm_); snd_pcm_uframes_t frames; char *iraw, **niraw; while(!buffer.stop() && !buffer.fail()) { snd_pcm_start(pcm_); if ((err = snd_pcm_wait (pcm_, -1)) < 0) { if (err == -EPIPE) { /* EPIPE means xrun */ snd_pcm_prepare(pcm_); } else if (err < 0) { LOGGER().error(E_READ_STREAM, snd_strerror(err)); snd_pcm_sw_params_free(sw_config_); close(); output->close(); return E_READ_STREAM; } } frames = snd_pcm_avail_update(pcm_); if(frames < 0) { LOGGER().error(E_READ_STREAM, snd_strerror(err)); snd_pcm_sw_params_free(sw_config_); close(); output->close(); return E_READ_STREAM; } if(frames > buffer.getBufferLength()) frames = buffer.getBufferLength(); int rc; getRawInputBuffers( buffer, &iraw, &niraw); // read from the stream if(organization_ == SND_PCM_ACCESS_RW_NONINTERLEAVED) { rc = snd_pcm_readn(pcm_, (void**)niraw, frames); } else { rc = snd_pcm_readi(pcm_, (void*)iraw, frames); } // save the number of frames read TSize lastwrite = ((TSize)rc <= buffer.getBufferLength())? rc : buffer.getBufferLength(); setBufferLastWrite(buffer, lastwrite); // detect errors if (rc == -EPIPE) { /* EPIPE means underrun */ snd_pcm_prepare(pcm_); } else if (rc < 0) { LOGGER().warning(E_READ_STREAM, snd_strerror(rc)); snd_pcm_sw_params_free(sw_config_); close(); output->close(); return E_READ_STREAM; } TErrors reterr; if( (reterr=output->write(buffer)) != E_OK ) { snd_pcm_sw_params_free(sw_config_); close(); output->close(); } } snd_pcm_sw_params_free(sw_config_); close(); output->close(); return E_OK; }
bool QAudioInputPrivate::open( QObject *input ) { // Open the Alsa capture device. bool rc = true; int err; unsigned int freakuency = frequency; if ((err = snd_pcm_open(&handle, m_device.constData(), //"plughw:0,0" SND_PCM_STREAM_CAPTURE, 0/*SND_PCM_ASYNC*/)) < 0) { qWarning( "QAudioInput: snd_pcm_open: error %d", err); rc = false; } else { snd_pcm_hw_params_t *hwparams; // We want non-blocking mode. snd_pcm_nonblock(handle, 1); // Set the desired parameters. snd_pcm_hw_params_alloca(&hwparams); err = snd_pcm_hw_params_any(handle, hwparams); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_any: err %d", err); } err = snd_pcm_hw_params_set_access(handle, hwparams, access); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_access: err %d",err); } err = snd_pcm_hw_params_set_format(handle, hwparams,format); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_format: err %d",err); } err = snd_pcm_hw_params_set_channels(handle,hwparams,(unsigned int)channels); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_channels: err %d",err); } err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &freakuency, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_rate_near: err %d",err); } if(freakuency > 1.05 * frequency || freakuency < 0.95 * frequency) { qWarning("QAudioInput: warning, sample rate %i not supported by the hardware, using %u", frequency, freakuency); } if ( samplesPerBlock != -1 ) { // Set buffer and period sizes based on the supplied block size. sample_size = (snd_pcm_uframes_t)( samplesPerBlock * channels / 8 ); err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &freakuency, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_rate_near: err %d",err); } if(freakuency > 1.05 * frequency || freakuency < 0.95 * frequency) { qWarning( "QAudioInput: warning, sample rate %i not supported by the hardware, using %u", frequency, freakuency); } err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err %d",err); } period_time = 1000000 * 256 / frequency; err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_period_time_near: err %d",err); } } else { // Use the largest buffer and period sizes we can. err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err %d",err); } period_time = 1000000 * 256 / frequency; err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params_set_period_time_near: err %d",err); } } err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_hw_params: err %d",err); } int dir; unsigned int vval, vval2; snd_pcm_access_t aval; snd_pcm_format_t fval; snd_pcm_subformat_t sval; qLog(QAudioInput) << "PCM handle name = " << snd_pcm_name(handle); qLog(QAudioInput) << "PCM state = " << snd_pcm_state_name(snd_pcm_state(handle)); snd_pcm_hw_params_get_access(hwparams,&aval); vval = (unsigned int)aval; if ( (int)vval != (int)access ) { qLog(QAudioInput) << QString("access type not set, want %1 got %2") .arg(snd_pcm_access_name((snd_pcm_access_t)access)) .arg(snd_pcm_access_name((snd_pcm_access_t)vval)); access = (snd_pcm_access_t)vval; } qLog(QAudioInput) << "access type = " << snd_pcm_access_name((snd_pcm_access_t)vval); snd_pcm_hw_params_get_format(hwparams, &fval); vval = (unsigned int)fval; if ( (int)vval != (int)format ) { qLog(QAudioInput) << QString("format type not set, want %1 got %2") .arg(snd_pcm_format_name((snd_pcm_format_t)format)) .arg(snd_pcm_format_name((snd_pcm_format_t)vval)); format = (snd_pcm_format_t)vval; } qLog(QAudioInput) << QString("format = '%1' (%2)") .arg(snd_pcm_format_name((snd_pcm_format_t)vval)) .arg(snd_pcm_format_description((snd_pcm_format_t)vval)) .toLatin1().constData(); snd_pcm_hw_params_get_subformat(hwparams,&sval); vval = (unsigned int)sval; qLog(QAudioInput) << QString("subformat = '%1' (%2)") .arg(snd_pcm_subformat_name((snd_pcm_subformat_t)vval)) .arg(snd_pcm_subformat_description((snd_pcm_subformat_t)vval)) .toLatin1().constData(); snd_pcm_hw_params_get_channels(hwparams, &vval); if ( (int)vval != (int)channels ) { qLog(QAudioInput) << QString("channels type not set, want %1 got %2").arg(channels).arg(vval); channels = vval; } qLog(QAudioInput) << "channels = " << vval; snd_pcm_hw_params_get_rate(hwparams, &vval, &dir); if ( (int)vval != (int)frequency ) { qLog(QAudioInput) << QString("frequency type not set, want %1 got %2").arg(frequency).arg(vval); frequency = vval; } qLog(QAudioInput) << "rate =" << vval << " bps"; snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); qLog(QAudioInput) << "period time =" << period_time << " us"; snd_pcm_hw_params_get_period_size(hwparams,&period_size, &dir); qLog(QAudioInput) << "period size =" << (int)period_size; snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); qLog(QAudioInput) << "buffer time =" << buffer_time; snd_pcm_hw_params_get_buffer_size(hwparams,(snd_pcm_uframes_t *) &buffer_size); qLog(QAudioInput) << "buffer size =" << (int)buffer_size; snd_pcm_hw_params_get_periods(hwparams, &vval, &dir); qLog(QAudioInput) << "periods per buffer =" << vval; snd_pcm_hw_params_get_rate_numden(hwparams, &vval, &vval2); qLog(QAudioInput) << QString("exact rate = %1/%2 bps").arg(vval).arg(vval2).toLatin1().constData(); vval = snd_pcm_hw_params_get_sbits(hwparams); qLog(QAudioInput) << "significant bits =" << vval; snd_pcm_hw_params_get_tick_time(hwparams,&vval, &dir); qLog(QAudioInput) << "tick time =" << vval; vval = snd_pcm_hw_params_is_batch(hwparams); qLog(QAudioInput) << "is batch =" << vval; vval = snd_pcm_hw_params_is_block_transfer(hwparams); qLog(QAudioInput) << "is block transfer =" << vval; vval = snd_pcm_hw_params_is_double(hwparams); qLog(QAudioInput) << "is double =" << vval; vval = snd_pcm_hw_params_is_half_duplex(hwparams); qLog(QAudioInput) << "is half duplex =" << vval; vval = snd_pcm_hw_params_is_joint_duplex(hwparams); qLog(QAudioInput) << "is joint duplex =" << vval; vval = snd_pcm_hw_params_can_overrange(hwparams); qLog(QAudioInput) << "can overrange =" << vval; vval = snd_pcm_hw_params_can_mmap_sample_resolution(hwparams); qLog(QAudioInput) << "can mmap =" << vval; vval = snd_pcm_hw_params_can_pause(hwparams); qLog(QAudioInput) << "can pause =" << vval; vval = snd_pcm_hw_params_can_resume(hwparams); qLog(QAudioInput) << "can resume =" << vval; vval = snd_pcm_hw_params_can_sync_start(hwparams); qLog(QAudioInput) << "can sync start =" << vval; snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_sw_params_current(handle, swparams); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_sw_params_current: err %d",err); } err = snd_pcm_sw_params_set_start_threshold(handle,swparams,period_size); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_sw_params_set_start_threshold: err %d",err); } err = snd_pcm_sw_params_set_avail_min(handle, swparams,period_size); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_sw_params_set_avail_min: err %d",err); } err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_sw_params_set_xfer_align: err %d",err); } err = snd_pcm_sw_params(handle, swparams); if ( err < 0 ) { qWarning( "QAudioInput: snd_pcm_sw_params: err %d",err); } snd_pcm_prepare(handle); snd_pcm_start(handle); int count = snd_pcm_poll_descriptors_count(handle); pollfd *pfds = new pollfd[count]; snd_pcm_poll_descriptors(handle, pfds, count); for (int i = 0; i < count; ++i) { if ((pfds[i].events & POLLIN) != 0) { notifier = new QSocketNotifier(pfds[i].fd, QSocketNotifier::Read); QObject::connect(notifier, SIGNAL(activated(int)), input, SIGNAL(readyRead())); break; } } if (notifier == NULL) { rc = false; } delete pfds; } return rc; }
int snd_pcm_generic_start(snd_pcm_t *pcm) { snd_pcm_generic_t *generic = pcm->private_data; return snd_pcm_start(generic->slave); }
void *CallbackThread( void *userData ) { PaAlsaStream *stream = (PaAlsaStream*)userData; pthread_cleanup_push( &Stop, stream ); // Execute Stop on exit if( stream->pcm_playback ) snd_pcm_start( stream->pcm_playback ); else if( stream->pcm_capture ) snd_pcm_start( stream->pcm_capture ); while(1) { int frames_avail; int frames_got; PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ int callbackResult; int framesProcessed; pthread_testcancel(); { /* calculate time info */ snd_timestamp_t capture_timestamp; snd_timestamp_t playback_timestamp; snd_pcm_status_t *capture_status; snd_pcm_status_t *playback_status; snd_pcm_status_alloca( &capture_status ); snd_pcm_status_alloca( &playback_status ); if( stream->pcm_capture ) { snd_pcm_status( stream->pcm_capture, capture_status ); snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); } if( stream->pcm_playback ) { snd_pcm_status( stream->pcm_playback, playback_status ); snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); } /* Hmm, we potentially have both a playback and a capture timestamp. * Hopefully they are the same... */ if( stream->pcm_capture && stream->pcm_playback ) { float capture_time = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); float playback_time= playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); if( fabsf(capture_time-playback_time) > 0.01 ) PA_DEBUG(("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time))); timeInfo.currentTime = capture_time; } else if( stream->pcm_playback ) { timeInfo.currentTime = playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); } else { timeInfo.currentTime = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); } if( stream->pcm_capture ) { snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status ); timeInfo.inputBufferAdcTime = timeInfo.currentTime - (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; } if( stream->pcm_playback ) { snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status ); timeInfo.outputBufferDacTime = timeInfo.currentTime + (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; } } /* IMPLEMENT ME: - handle buffer slips */ /* depending on whether the host buffers are interleaved, non-interleaved or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(), PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here. */ framesProcessed = frames_avail = wait( stream ); while( frames_avail > 0 ) { //PA_DEBUG(( "%d frames available\n", frames_avail )); /* Now we know the soundcard is ready to produce/receive at least * one period. We just need to get the buffers for the client * to read/write. */ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* @todo pass underflow/overflow flags when necessary */ ); frames_got = setup_buffers( stream, frames_avail ); PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); callbackResult = paContinue; /* this calls the callback */ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); /* inform ALSA how many frames we wrote */ if( stream->pcm_capture ) snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_got ); if( stream->pcm_playback ) snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_got ); if( callbackResult != paContinue ) break; frames_avail -= frames_got; } /* If you need to byte swap outputBuffer, you can do it here using routines in pa_byteswappers.h */ if( callbackResult != paContinue ) { stream->callback_finished = 1; stream->callbackAbort = (callbackResult == paAbort); pthread_exit( NULL ); } } /* This code is unreachable, but important to include regardless because it * is possibly a macro with a closing brace to match the opening brace in * pthread_cleanup_push() above. The documentation states that they must * always occur in pairs. */ pthread_cleanup_pop( 1 ); }
static int pcm_start(pcm_handle_t* pcm) { return snd_pcm_start(pcm->pcm); }
/***************************************************************************** * OpenAudioDev: open and set up the audio device and probe for capabilities *****************************************************************************/ static int OpenAudioDevAlsa( demux_t *p_demux, const char *psz_device ) { demux_sys_t *p_sys = p_demux->p_sys; p_sys->p_alsa_pcm = NULL; snd_pcm_hw_params_t *p_hw_params = NULL; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t chunk_size; /* ALSA */ int i_err; if( ( i_err = snd_pcm_open( &p_sys->p_alsa_pcm, psz_device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK ) ) < 0) { msg_Err( p_demux, "Cannot open ALSA audio device %s (%s)", psz_device, snd_strerror( i_err ) ); goto adev_fail; } if( ( i_err = snd_pcm_nonblock( p_sys->p_alsa_pcm, 1 ) ) < 0) { msg_Err( p_demux, "Cannot set ALSA nonblock (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Begin setting hardware parameters */ if( ( i_err = snd_pcm_hw_params_malloc( &p_hw_params ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot allocate hardware parameter structure (%s)", snd_strerror( i_err ) ); goto adev_fail; } if( ( i_err = snd_pcm_hw_params_any( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot initialize hardware parameter structure (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Set Interleaved access */ if( ( i_err = snd_pcm_hw_params_set_access( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot set access type (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Set 16 bit little endian */ if( ( i_err = snd_pcm_hw_params_set_format( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_FORMAT_S16_LE ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot set sample format (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Set sample rate */ i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, &p_sys->i_sample_rate, NULL ); if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set sample rate (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Set channels */ unsigned int channels = p_sys->b_stereo ? 2 : 1; if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 ) { channels = ( channels==1 ) ? 2 : 1; msg_Warn( p_demux, "ALSA: cannot set channel count (%s). " "Trying with channels=%d", snd_strerror( i_err ), channels ); if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot set channel count (%s)", snd_strerror( i_err ) ); goto adev_fail; } p_sys->b_stereo = ( channels == 2 ); } /* Set metrics for buffer calculations later */ unsigned int buffer_time; if( ( i_err = snd_pcm_hw_params_get_buffer_time_max(p_hw_params, &buffer_time, 0) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot get buffer time max (%s)", snd_strerror( i_err ) ); goto adev_fail; } if( buffer_time > 500000 ) buffer_time = 500000; /* Set period time */ unsigned int period_time = buffer_time / 4; i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, &period_time, 0 ); if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set period time (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Set buffer time */ i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, &buffer_time, 0 ); if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set buffer time (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Apply new hardware parameters */ if( ( i_err = snd_pcm_hw_params( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot set hw parameters (%s)", snd_strerror( i_err ) ); goto adev_fail; } /* Get various buffer metrics */ snd_pcm_hw_params_get_period_size( p_hw_params, &chunk_size, 0 ); snd_pcm_hw_params_get_buffer_size( p_hw_params, &buffer_size ); if( chunk_size == buffer_size ) { msg_Err( p_demux, "ALSA: period cannot equal buffer size (%lu == %lu)", chunk_size, buffer_size); goto adev_fail; } int bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE); int bits_per_frame = bits_per_sample * channels; p_sys->i_alsa_chunk_size = chunk_size; p_sys->i_alsa_frame_size = bits_per_frame / 8; p_sys->i_max_frame_size = chunk_size * bits_per_frame / 8; snd_pcm_hw_params_free( p_hw_params ); p_hw_params = NULL; /* Prep device */ if( ( i_err = snd_pcm_prepare( p_sys->p_alsa_pcm ) ) < 0 ) { msg_Err( p_demux, "ALSA: cannot prepare audio interface for use (%s)", snd_strerror( i_err ) ); goto adev_fail; } snd_pcm_start( p_sys->p_alsa_pcm ); return VLC_SUCCESS; adev_fail: if( p_hw_params ) snd_pcm_hw_params_free( p_hw_params ); if( p_sys->p_alsa_pcm ) snd_pcm_close( p_sys->p_alsa_pcm ); p_sys->p_alsa_pcm = NULL; return VLC_EGENERIC; }
/* return 0 on success */ int alsa_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate, int blocksize) { int err, inchans = 0, outchans = 0, subunitdir; char devname[512]; snd_output_t* out; int frag_size = (blocksize ? blocksize : ALSA_DEFFRAGSIZE); int nfrags, i, iodev, dev2; int wantinchans, wantoutchans, device; nfrags = sys_schedadvance * (float)rate / (1e6 * frag_size); /* save our belief as to ALSA's buffer size for later */ alsa_buf_samps = nfrags * frag_size; alsa_nindev = alsa_noutdev = 0; alsa_jittermax = ALSA_DEFJITTERMAX; if (sys_verbose) post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); for (iodev = 0; iodev < naudioindev; iodev++) { alsa_numbertoname(audioindev[iodev], devname, 512); err = snd_pcm_open(&alsa_indev[alsa_nindev].a_handle, devname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); check_error(err, 0, "snd_pcm_open"); if (err < 0) continue; alsa_indev[alsa_nindev].a_devno = audioindev[iodev]; snd_pcm_nonblock(alsa_indev[alsa_nindev].a_handle, 1); if (sys_verbose) post("opened input device name %s", devname); alsa_nindev++; } for (iodev = 0; iodev < naudiooutdev; iodev++) { alsa_numbertoname(audiooutdev[iodev], devname, 512); err = snd_pcm_open(&alsa_outdev[alsa_noutdev].a_handle, devname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); check_error(err, 1, "snd_pcm_open"); if (err < 0) continue; alsa_outdev[alsa_noutdev].a_devno = audiooutdev[iodev]; snd_pcm_nonblock(alsa_outdev[alsa_noutdev].a_handle, 1); alsa_noutdev++; } if (!alsa_nindev && !alsa_noutdev) goto blewit; /* If all the open devices support mmap_noninterleaved, let's call Wini's code in s_audio_alsamm.c */ alsa_usemmap = 1; for (iodev = 0; iodev < alsa_nindev; iodev++) if (!alsaio_canmmap(&alsa_indev[iodev])) alsa_usemmap = 0; for (iodev = 0; iodev < alsa_noutdev; iodev++) if (!alsaio_canmmap(&alsa_outdev[iodev])) alsa_usemmap = 0; if (alsa_usemmap) { post("using mmap audio interface"); if (alsamm_open_audio(rate, blocksize)) goto blewit; else return (0); } for (iodev = 0; iodev < alsa_nindev; iodev++) { int channels = chindev[iodev]; if (alsaio_setup(&alsa_indev[iodev], 0, &channels, &rate, nfrags, frag_size) < 0) goto blewit; inchans += channels; } for (iodev = 0; iodev < alsa_noutdev; iodev++) { int channels = choutdev[iodev]; if (alsaio_setup(&alsa_outdev[iodev], 1, &channels, &rate, nfrags, frag_size) < 0) goto blewit; outchans += channels; } if (!inchans && !outchans) goto blewit; for (iodev = 0; iodev < alsa_nindev; iodev++) snd_pcm_prepare(alsa_indev[iodev].a_handle); for (iodev = 0; iodev < alsa_noutdev; iodev++) snd_pcm_prepare(alsa_outdev[iodev].a_handle); /* if duplex we can link the channels so they start together */ for (iodev = 0; iodev < alsa_nindev; iodev++) for (dev2 = 0; dev2 < alsa_noutdev; dev2++) { if (alsa_indev[iodev].a_devno == alsa_outdev[iodev].a_devno) { snd_pcm_link(alsa_indev[iodev].a_handle, alsa_outdev[iodev].a_handle); } } /* allocate the status variables */ if (!alsa_status) { err = snd_pcm_status_malloc(&alsa_status); check_error(err, -1, "snd_pcm_status_malloc"); } /* fill the buffer with silence and prime the output FIFOs. This should automatically start the output devices. */ memset(alsa_snd_buf, 0, alsa_snd_bufsize); if (outchans) { i = (frag_size * nfrags)/DEFDACBLKSIZE + 1; while (i--) { for (iodev = 0; iodev < alsa_noutdev; iodev++) snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, DEFDACBLKSIZE); } } if (inchans) { /* some of the ADC devices might already have been started by starting the outputs above, but others might still need it. */ for (iodev = 0; iodev < alsa_nindev; iodev++) if (snd_pcm_state(alsa_indev[iodev].a_handle) != SND_PCM_STATE_RUNNING) if ((err = snd_pcm_start(alsa_indev[iodev].a_handle)) < 0) check_error(err, -1, "input start failed"); } return (0); blewit: STUFF->st_inchannels = 0; STUFF->st_outchannels = 0; alsa_close_audio(); return (1); }
void AlsaLayer::startCaptureStream() { if (captureHandle_ and not is_capture_running_) if (ALSA_CALL(snd_pcm_start(captureHandle_), "Couldn't start capture") >= 0) is_capture_running_ = true; }
int main() { char *filename = "44k.wav"; SF_INFO sfinfo; SNDFILE *f; f = sf_open(filename,SFM_READ, &sfinfo); printf("\nCHECK RATE %d \n",sfinfo.samplerate); printf("\nCHECK CHANNELS %d \n",sfinfo.channels); printf("\nNumber of frames %ld \n",sfinfo.frames); int j; snd_pcm_t *pcm_handle; char *pcm_name; unsigned int err; //pcm_name = "default"; pcm_name = "plughw:0,0"; snd_pcm_hw_params_t *hwparams; unsigned int rate = sfinfo.samplerate; unsigned int channels = sfinfo.channels; unsigned int exact_rate; snd_pcm_uframes_t frames, offset; snd_pcm_sframes_t commitres, avail,size=170; const snd_pcm_channel_area_t *areas; unsigned char *ptr[2]; snd_pcm_format_t stream = SND_PCM_FORMAT_S16_LE; err=snd_pcm_open(&pcm_handle,pcm_name,SND_PCM_STREAM_PLAYBACK,0); if(err<0) { perror("\nCannot open PCM device\n"); exit(0); } if(snd_pcm_hw_params_malloc(&hwparams)<0) { perror("\nMemory cannot be allocated\n"); exit(0); } if(snd_pcm_hw_params_any(pcm_handle,hwparams)<0) { perror("\nUnable to set any HW configuration to device\n"); exit(0); } exact_rate = rate; if(snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)<0) { printf("\nUnable to set rate %u instead rate used is %u \n",rate,exact_rate); } rate = exact_rate; printf("\nExact rate is %u \n",rate); if(snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels),0) { perror("\nUnable to set channels\n"); exit(0); } if(snd_pcm_hw_params_set_format(pcm_handle, hwparams, stream)<0) { perror("\nUnable to set format\n"); exit(0); } if(snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)<0) { perror("\nUnable to set access\n"); exit(0); } snd_pcm_sframes_t period_size; snd_pcm_hw_params_get_period_size(hwparams, &period_size,0); printf("\nPeriod size is %ld \n",period_size); err = snd_pcm_hw_params(pcm_handle,hwparams); if(err<0) { perror("\nHW params cannot be set\n"); exit(0); } snd_pcm_hw_params_get_period_size(hwparams,&period_size,0); printf("\nObtained Period size = %ld \n",period_size); size = period_size; snd_pcm_hw_params_get_buffer_size(hwparams,&frames); printf("\nObtained buffer size is %ld \n",frames); int ret = open(filename,O_RDONLY); if(ret<0) { perror("\nFile cannot be opened\n"); exit(0); } int k=0; int i; while(1) { k++; if(k==2) { err = snd_pcm_start(pcm_handle); if(err<0) { perror("\nError starting PCM device\n"); exit(1); } } avail = snd_pcm_avail_update(pcm_handle); if(avail<frames) { err = snd_pcm_prepare(pcm_handle); if(err<0) { perror("\nNo frames available\n"); exit(0); } } // printf("\nFrames available %ld\n",avail); size=period_size; while(size>0) { frames = size; err = snd_pcm_mmap_begin(pcm_handle, &areas, &offset, &frames); if(err<0) { perror("\nMMAP cannot assign areas\n"); exit(0); } for(i=0;i<1;i++) { ptr[i] = (unsigned char *)areas[i].addr + areas[i].first/8 + offset*(areas[i].step/8) ; // printf("\nCheck\n"); if(ptr[i]==NULL) { perror("\nPointer cannot point to memory\n"); exit(0); } } for(i=0;i<1;i++) if(read(ret, ptr[i], frames*4)>0) { } else { perror("\nFile cannot be opened\n"); exit(0); } commitres = snd_pcm_mmap_commit(pcm_handle, offset, frames); if(commitres<0) { err = snd_pcm_prepare(pcm_handle); if(err<0) { perror("\nFrames cannot be committed\n"); exit(0); } } else size-=frames; snd_pcm_wait(pcm_handle,1000); }//END OF "while(size>0)" LOOP }//END OF OUTER WHILE LOOP }
static void *Thread (void *data) { demux_t *demux = data; demux_sys_t *sys = demux->p_sys; snd_pcm_t *pcm = sys->pcm; size_t bytes; int canc, val; canc = vlc_savecancel (); bytes = snd_pcm_frames_to_bytes (pcm, sys->period_size); val = snd_pcm_start (pcm); if (val) { msg_Err (demux, "cannot prepare device: %s", snd_strerror (val)); return NULL; } for (;;) { block_t *block = block_Alloc (bytes); if (unlikely(block == NULL)) break; /* Wait for data */ Poll (pcm, canc); /* Read data */ snd_pcm_sframes_t frames, delay; mtime_t pts; frames = snd_pcm_readi (pcm, block->p_buffer, sys->period_size); pts = mdate (); if (frames < 0) { if (frames == -EAGAIN) continue; val = snd_pcm_recover (pcm, frames, 1); if (val == 0) { msg_Warn (demux, "cannot read samples: %s", snd_strerror (frames)); continue; } msg_Err (demux, "cannot recover record stream: %s", snd_strerror (val)); DumpDeviceStatus (demux, pcm); break; } /* Compute time stamp */ if (snd_pcm_delay (pcm, &delay)) delay = 0; delay += frames; pts -= (CLOCK_FREQ * delay) / sys->rate; block->i_buffer = snd_pcm_frames_to_bytes (pcm, frames); block->i_nb_samples = frames; block->i_pts = pts; block->i_length = (CLOCK_FREQ * frames) / sys->rate; es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts); es_out_Send (demux->out, sys->es, block); } return NULL; }
/** Fill buffers, for starting and stopping * Alsa won't start playing until everything is filled up * This also updates mmap_pos * * Returns: Amount of periods in use so snd_pcm_avail_update * doesn't have to be called up to 4x in GetPosition() */ static snd_pcm_uframes_t CommitAll(IDsCaptureDriverBufferImpl *This, DWORD forced) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t used; const snd_pcm_uframes_t commitahead = This->mmap_buflen_frames; used = This->mmap_buflen_frames - snd_pcm_avail_update(This->pcm); TRACE("%p needs to commit to %lu, used: %lu\n", This, commitahead, used); if (used < commitahead && (forced || This->play_looping)) { snd_pcm_uframes_t done, putin = commitahead - used; if (This->mmap) { snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); CopyData(This, This->mmap_pos, putin); done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); This->mmap_pos += done; used += done; putin = commitahead - used; if (This->mmap_pos == This->mmap_buflen_frames && (snd_pcm_sframes_t)putin > 0 && This->play_looping) { This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); This->mmap_ofs_bytes %= This->mmap_buflen_bytes; snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); CopyData(This, This->mmap_pos, putin); done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); This->mmap_pos += done; used += done; } } else { DWORD pos; snd_pcm_sframes_t ret; snd_pcm_uframes_t cap = snd_pcm_bytes_to_frames(This->pcm, This->mmap_buflen_bytes); pos = realpos_to_fakepos(This, This->mmap_pos); if (This->mmap_pos + putin > cap) putin = cap - This->mmap_pos; ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); if (ret == -EPIPE) { WARN("Underrun occurred\n"); snd_pcm_prepare(This->pcm); ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); snd_pcm_start(This->pcm); } if (ret < 0) { WARN("Committing data: %ld / %s (%ld)\n", ret, snd_strerror(ret), putin); ret = 0; } This->mmap_pos += ret; used += ret; /* At this point mmap_pos may be >= This->mmap_pos this is harmless * realpos_to_fakepos handles it well, and below it is truncated */ putin = commitahead - used; if (putin > 0) { pos = realpos_to_fakepos(This, This->mmap_pos); ret = snd_pcm_readi(This->pcm, This->presented_buffer + pos, putin); if (ret > 0) { This->mmap_pos += ret; used += ret; } } } } if (This->mmap_pos >= This->mmap_buflen_frames) { This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); This->mmap_ofs_bytes %= This->mmap_buflen_bytes; This->mmap_pos -= This->mmap_buflen_frames; } return used; }
/// Open and init default sound card params int init_soundcard() { int err = 0; if ((err = snd_pcm_open(&capture_handle, snd_device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { fprintf(stderr, "cannot open audio device %s (%s, %d)\n", snd_device, snd_strerror(err), err); return OPEN_ERROR; } if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { fprintf( stderr, "cannot allocate hardware parameter structure (%s, %d)\n", snd_strerror(err), err); return MALLOC_ERROR; } if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) { fprintf( stderr, "cannot initialize hardware parameter structure (%s, %d)\n", snd_strerror(err), err); return ANY_ERROR; } if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf(stderr, "cannot set access type (%s, %d)\n", snd_strerror(err), err); return ACCESS_ERROR; } if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { fprintf(stderr, "cannot set sample format (%s, %d)\n", snd_strerror(err), err); return FORMAT_ERROR; } if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &srate, 0)) < 0) { fprintf(stderr, "cannot set sample rate (%s, %d)\n", snd_strerror(err), err); return RATE_ERROR; } if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, nchan)) < 0) { fprintf(stderr, "cannot set channel count (%s, %d)\n", snd_strerror(err), err); return CHANNELS_ERROR; } if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) { fprintf(stderr, "cannot set parameters (%s, %d)\n", snd_strerror(err), err); return PARAMS_ERROR; } if ((err = snd_pcm_prepare(capture_handle)) < 0) { fprintf( stderr, "cannot prepare audio interface for use (%s, %d)\n", snd_strerror(err), err); return PREPARE_ERROR; } //fprintf(stderr, "Capture handle1: %x\n", capture_handle); //fprintf(stderr, "Capture state1: %d\n", snd_pcm_state(capture_handle)); // Start reading data from sound card if ((err = snd_pcm_start(capture_handle)) < 0) { fprintf(stderr, "cannot start soundcard (%s, %d)\n", snd_strerror(err), err); return START_ERROR; } //fprintf(stderr, "Capture handle2: %x\n", capture_handle); //fprintf(stderr, "Capture state2: %d\n", snd_pcm_state(capture_handle)); /* fprintf(stderr, "%s\n", "Parameters of PCM:"); fprintf(stderr, "%x\n", capture_handle); fprintf(stderr, "%s\n", snd_pcm_name(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_type(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_stream(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_poll_descriptors_count(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_state(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_avail(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_avail_update(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_rewindable(capture_handle)); fprintf(stderr, "%d\n", snd_pcm_forwardable(capture_handle)); fprintf(stderr, "%s\n", "-------------------------------------"); fprintf(stderr, "%d\n", snd_pcm_info_malloc(&s_info)); fprintf(stderr, "%d\n", snd_pcm_info(capture_handle, s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_device(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_subdevice(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_stream(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_card(s_info)); fprintf(stderr, "%s\n", snd_pcm_info_get_id(s_info)); fprintf(stderr, "%s\n", snd_pcm_info_get_name(s_info)); fprintf(stderr, "%s\n", snd_pcm_info_get_subdevice_name(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_class(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_subclass(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_subdevices_count(s_info)); fprintf(stderr, "%d\n", snd_pcm_info_get_subdevices_avail(s_info)); fprintf(stderr, "%s\n", "-------------------------------------"); fprintf(stderr, "%d\n", snd_pcm_hw_params_current(capture_handle, hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_double(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_batch(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_block_transfer(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_monotonic(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_can_overrange(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_can_pause(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_can_resume(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_half_duplex(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_is_joint_duplex(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_can_sync_start(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_can_disable_period_wakeup(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_sbits(hw_params)); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_fifo_size(hw_params)); fprintf(stderr, "%s\n", "-------------------------------------"); unsigned int *tmp1 = (unsigned int *)malloc(sizeof(unsigned int)); int *tmp2 = (int *)malloc(sizeof(int)); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_channels(hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_channels_min(hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_channels_max(hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_rate(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_rate_min(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_rate_max(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_rate_resample(capture_handle, hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_export_buffer(capture_handle, hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_period_wakeup(capture_handle, hw_params, tmp1)); fprintf(stderr, "%d\n", *tmp1); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_period_time(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_period_time_min(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); fprintf(stderr, "%d\n", snd_pcm_hw_params_get_period_time_max(hw_params, tmp1, tmp2)); fprintf(stderr, "%d\n", *tmp1 , *tmp2); */ snd_pcm_hw_params_free(hw_params); /* snd_pcm_info_free(s_info); free(tmp1); free(tmp2); */ return NO_ERROR; }
int audio_alsa_init() { int fd, err; char *pcm_rate; char tmp_name[20]; init_rec_buffer(); /* Create a temporary filename for our FIFO, * Use mkstemp() instead of mktemp() although we need a FIFO not a * regular file. We do this since glibc barfs at mktemp() and this * scares the users :-) */ strcpy(tmp_name, "/tmp/lircXXXXXX"); fd = mkstemp(tmp_name); close(fd); /* Start the race! */ unlink(tmp_name); if (mknod(tmp_name, S_IFIFO | S_IRUSR | S_IWUSR, 0)) { logprintf(LOG_ERR, "could not create FIFO %s", tmp_name); logperror(LOG_ERR, "audio_alsa_init ()"); return 0; } /* Phew, we won the race ... */ /* Open the pipe and hand it to LIRC ... */ hw.fd = open(tmp_name, O_RDWR); if (hw.fd < 0) { logprintf(LOG_ERR, "could not open pipe %s", tmp_name); logperror(LOG_ERR, "audio_alsa_init ()"); error: unlink(tmp_name); audio_alsa_deinit(); return 0; } /* Open the other end of the pipe and hand it to ALSA code. * We're opening it in non-blocking mode to avoid lockups. */ alsa_hw.fd = open(tmp_name, O_RDWR | O_NONBLOCK); /* Ok, we don't need the FIFO visible in the filesystem anymore ... */ unlink(tmp_name); /* Examine the device name, if it contains a sample rate */ strncpy(tmp_name, hw.device, sizeof(tmp_name) - 1); pcm_rate = strchr(tmp_name, '@'); if (pcm_rate) { int rate; char *stereo_channel; /* Examine if we need to capture in stereo * looking for an 'l' or 'r' character to indicate * which channel to look at.*/ stereo_channel = strchr(pcm_rate, ','); if (stereo_channel) { /* Syntax in device string indicates we need to use stereo */ alsa_hw.num_channels = 2; /* As we are requesting stereo now, use the more common signed 16bit samples */ alsa_hw.format = SND_PCM_FORMAT_S16_LE; if (stereo_channel[1] == 'l') { alsa_hw.channel = 0; } else if (stereo_channel[1] == 'r') { alsa_hw.channel = 1; } else { logperror(LOG_WARNING, "dont understand which channel to use - defaulting to left\n"); } } /* Remove the sample rate from device name (and channel indicator if present) */ *pcm_rate++ = 0; /* See if rate is meaningful */ rate = atoi(pcm_rate); if (rate > 0) { alsa_hw.rate = rate; } } /* Open the audio card in non-blocking mode */ err = snd_pcm_open(&alsa_hw.handle, tmp_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (err < 0) { logprintf(LOG_ERR, "could not open audio device %s: %s", hw.device, snd_strerror(err)); logperror(LOG_ERR, "audio_alsa_init ()"); goto error; } /* Set up the I/O signal handler */ if (alsa_error ("async_add_handler", snd_async_add_pcm_handler(&alsa_hw.sighandler, alsa_hw.handle, alsa_sig_io, NULL))) goto error; /* Set sampling parameters */ if (alsa_set_hwparams(alsa_hw.handle)) goto error; LOGPRINTF(LOG_INFO, "hw_audio_alsa: Using device '%s', sampling rate %dHz\n", tmp_name, alsa_hw.rate); /* Start sampling data */ if (alsa_error("start", snd_pcm_start(alsa_hw.handle))) goto error; return 1; }
int main(int argc, char* argv[]) { int df, wf; if(argc<2) { printf("Usage: %s DM-file [wav-file]\n",argv[0]); exit(0); } dminfo=malloc(sizeof(dmarg)); wi=malloc(sizeof(wavinfo)); if(!dminfo||!wi) { printf("Memory Allocation Error!\n"); exit(0); } fp=fopen(argv[1],"rb"); if(!fp) { printf("Error opening input file %s!\n", argv[1]); exit(0); } fread(dminfo,sizeof(dmarg),1,fp); if(dminfo->magicnum!=MAGICNUM) { printf("not a dm file!\n"); exit(0); } if(dminfo->delta<0x10000) printf("DM delta value %d\n",dminfo->delta); else { int deltal=dminfo->delta&0xffff; int deltar=(dminfo->delta-deltal)/0x10000; printf("DM delta value %d/%d\n",deltal,deltar); } if(dminfo->mode==MODE_TYPE1) printf("Channel Delta Mode\n"); if(dminfo->mode==MODE_TYPE2) printf("L+R L-R Mode\n"); fread(wi,sizeof(wavinfo),1,fp); printf("Sample rate %dHz, %d bits, %d channels\n",wi->srate,wi->bits,wi->channel); printf("Length %d:%d\n",(wi->samples/wi->srate)/60,(wi->samples/wi->srate)%60); initsound(wi->srate, wi->channel); snd_pcm_prepare(pcm_handle); decodeDM(fp, wi, dminfo, buf, BUFFER_NUM*frames*2*wi->channel); snd_pcm_writei (pcm_handle, buf, BUFFER_NUM*frames); snd_pcm_start(pcm_handle); while(1) { df=decodeDM(fp, wi, dminfo, buf, BUFFER_NUM*frames*2*wi->channel); wf = snd_pcm_writei (pcm_handle, buf, df); if(df<BUFFER_NUM*frames) break; } snd_pcm_drain(pcm_handle); snd_pcm_close(pcm_handle); free(buf); }
static void alsa_sig_io(snd_async_handler_t * h) { /* Previous sample */ static unsigned char ps = 0x80; /* Count samples with similar level (to detect pule/space length), 24.8 fp */ static unsigned sample_count = 0; /* Current signal level (dynamically changes) */ static unsigned signal_level = 0; /* Current state (pulse or space) */ static unsigned signal_state = 0; /* Signal maximum and minimum (used for "zero" detection) */ static unsigned char signal_max = 0x80, signal_min = 0x80; /* Non-zero if we're in zero crossing waiting state */ static char waiting_zerox = 0; /* Store sample size, as our sample buffer will represent shorts or chars */ unsigned char bytes_per_sample = (alsa_hw.format == SND_PCM_FORMAT_S16_LE ? 2 : 1); int i, err; char buff[READ_BUFFER_SIZE]; snd_pcm_sframes_t count; /* The value to multiply with number of samples to get microseconds * (fixed-point 24.8 bits). */ unsigned mulconst = 256000000 / alsa_hw.rate; /* Maximal number of samples that can be multiplied by mulconst */ unsigned maxcount = (((PULSE_MASK << 8) | 0xff) / mulconst) << 8; /* First of all, check for underrun. This happens, for example, when * the X11 server starts. If we won't, recording will stop forever. */ snd_pcm_state_t state = snd_pcm_state(alsa_hw.handle); switch (state) { case SND_PCM_STATE_SUSPENDED: while ((err = snd_pcm_resume(alsa_hw.handle)) == -EAGAIN) /* wait until the suspend flag is released */ sleep(1); if (err >= 0) goto var_reset; /* Fallthrough */ case SND_PCM_STATE_XRUN: alsa_error("prepare", snd_pcm_prepare(alsa_hw.handle)); alsa_error("start", snd_pcm_start(alsa_hw.handle)); var_reset: /* Reset variables */ sample_count = 0; waiting_zerox = 0; signal_level = 0; signal_state = 0; signal_max = signal_min = 0x80; break; default: /* Stream is okay */ break; } /* Read all available data */ if ((count = snd_pcm_avail_update(alsa_hw.handle)) > 0) { if (count > (READ_BUFFER_SIZE / (bytes_per_sample * alsa_hw.num_channels))) count = READ_BUFFER_SIZE / (bytes_per_sample * alsa_hw.num_channels); count = snd_pcm_readi(alsa_hw.handle, buff, count); /*Loop around samples, if stereo we are *only interested in one channel*/ for (i = 0; i < count; i++) { /* cs == current sample */ unsigned char cs, as, sl, sz, xz; if (bytes_per_sample == 2) { cs = ((*(short *) &buff[i * bytes_per_sample * alsa_hw.num_channels + bytes_per_sample * alsa_hw.channel]) >> 8); cs ^= 0x80; } else { cs = buff[i]; /* Convert signed samples to unsigned */ if (alsa_hw.format == SND_PCM_FORMAT_S8) { cs ^= 0x80; } } /* Track signal middle value (it could differ from 0x80) */ sz = (signal_min + signal_max) / 2; if (cs <= sz) signal_min = (signal_min * 7 + cs) / 8; if (cs >= sz) signal_max = (signal_max * 7 + cs) / 8; /* Compute the absolute signal deviation from middle */ as = U8_ABSDIFF(cs, sz); /* Integrate incoming signal (auto level adjustment) */ signal_level = (signal_level * 7 + as) / 8; /* Don't let too low signal levels as it makes us sensible to noise */ sl = signal_level; if (sl < 16) sl = 16; /* Detect crossing current "zero" level */ xz = ((cs - sz) ^ (ps - sz)) & 0x80; /* Don't wait for zero crossing for too long */ if (waiting_zerox && !xz) waiting_zerox--; /* Detect significant signal level changes */ if ((abs(cs - ps) > sl / 2) && xz) waiting_zerox = 2; /* If we have crossed zero with a substantial level change, go */ if (waiting_zerox && xz) { lirc_t x; waiting_zerox = 0; if (sample_count >= maxcount) { x = PULSE_MASK; sample_count = 0; } else { /** * Try to interpolate the samples and determine where exactly * the zero crossing point was. This is required as the * remote signal frequency is relatively close to our sampling * frequency thus a sampling error of 1 sample can lead to * substantial time differences. * * slope = (x2 - x1) / (y2 - y1) * x = x1 + (y - y1) * slope * * where x1=-1, x2=0, y1=ps, y2=cs, y=sz, thus: * * x = -1 + (y - y1) / (y2 - y1), or * ==> x = (y - y2) / (y2 - y1) * * y2 (cs) cannot be equal to y1 (ps), otherwise we wouldn't * get here. */ int delta = (((int)sz - (int)cs) << 8) / ((int)cs - (int)ps); /* This expression can easily overflow the 'long' value since it * multiplies two 24.8 values (and we get a 24.16 instead). * To avoid this we cast the intermediate value to "long long". */ x = (((long long)sample_count + delta) * mulconst) >> 16; /* The rest of the quantum is on behalf of next pulse. Note that * sample_count can easily be assigned here a negative value (in * the case zero crossing occurs during the next quantum). */ sample_count = -delta; } /* Consider impossible pulses with length greater than * 0.02 seconds, thus it is a space (desynchronization). */ if ((x > 20000) && signal_state) { signal_state = 0; LOGPRINTF(1, "Pulse/space desynchronization fixed - len %u", x); } x |= signal_state; /* Write the LIRC code to the FIFO */ write(alsa_hw.fd, &x, sizeof(x)); signal_state ^= PULSE_BIT; }
static int _ksnd_pcm_writei1(snd_pcm_substream_t *substream, unsigned long data, snd_pcm_uframes_t size, int srcchannels, transfer_f transfer) { snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; int err = 0; if (size == 0) return 0; snd_pcm_stream_lock_irq(substream); switch (_ksnd_pcm_state(substream)) { case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PAUSED: break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: err = -EBADFD; goto _end_unlock; } while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; avail = _ksnd_pcm_avail_update(substream); #if defined(__TDT__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) // attribute xfer_align is not used any more /* #warning Do we have to check the values 'size' and 'avail'? */ if ((avail < runtime->control->avail_min) && (size > avail)) { #else if (((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align))) { #endif int res; snd_pcm_stream_unlock_irq(substream); do { res = _ksnd_pcm_wait(substream, 10000); } while (res == 0 && _ksnd_pcm_state(substream) != SNDRV_PCM_STATE_PREPARED && _ksnd_pcm_state(substream) != SNDRV_PCM_STATE_PAUSED); snd_pcm_stream_lock_irq(substream); if (res == 0) /* timeout */ { if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_SUSPENDED) { err = -ESTRPIPE; goto _end_unlock; } else { snd_printd("playback write error " "(DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; } } else if (res < 0) /* error */ { err = res; goto _end_unlock; } avail = snd_pcm_playback_avail(runtime); } if (avail > runtime->min_align) avail -= avail % runtime->min_align; frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); err = transfer(substream, appl_ofs, data, offset, frames, srcchannels); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end; switch (_ksnd_pcm_state(substream)) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: break; } appl_ptr += frames; if (appl_ptr >= runtime->boundary) { runtime->control->appl_ptr = 0; } else { runtime->control->appl_ptr = appl_ptr; } if (substream->ops->ack) substream->ops->ack(substream); offset += frames; size -= frames; xfer += frames; if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_PREPARED && snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t) runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) goto _end_unlock; } } _end_unlock: snd_pcm_stream_unlock_irq(substream); _end: return xfer > 0 ? (snd_pcm_sframes_t) xfer : err; } int ksnd_pcm_writei(ksnd_pcm_t *kpcm, int *data, unsigned int size, unsigned int srcchannels) { snd_pcm_substream_t *substream = kpcm->substream; snd_pcm_runtime_t *runtime; int err; transfer_f out_func = 0; runtime = substream->runtime; if (substream->pcm->card->number == 2) { out_func = _ksnd_pcm_IEC60958_transfer; } else { out_func = _ksnd_pcm_write_transfer; } if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; if (size == 0) return 0; do { err = _ksnd_pcm_writei1(substream, (unsigned long)data, size, srcchannels, out_func); if (err < 0) { if (err == -EAGAIN) { continue; } if (err == -EPIPE) { printk("ALSA Aud underrun for hw:%d,%d\n", substream->pcm->card->number, substream->pcm->device); if ((err = ksnd_pcm_prepare(kpcm)) < 0) return err; continue; } return err; } else { data += samples_to_bytes(runtime, err * srcchannels); size -= err; } } while (size > 0); return 0; }
int alsa_send_dacs(void) { static double timenow; double timelast; t_sample *fp, *fp1, *fp2; int i, j, k, err, iodev, result, ch, resync = 0;; int chansintogo, chansouttogo; unsigned int transfersize; if (alsa_usemmap) return (alsamm_send_dacs()); if (!alsa_nindev && !alsa_noutdev) return (SENDDACS_NO); chansintogo = STUFF->st_inchannels; chansouttogo = STUFF->st_outchannels; transfersize = DEFDACBLKSIZE; timelast = timenow; timenow = sys_getrealtime(); #ifdef DEBUG_ALSA_XFER if (timenow - timelast > 0.050) post("long wait between calls: %d", (int)(1000 * (timenow - timelast))), fflush(stderr); callno++; #endif for (iodev = 0; iodev < alsa_nindev; iodev++) { result = snd_pcm_state(alsa_indev[iodev].a_handle); if (result == SND_PCM_STATE_XRUN) { int res2 = snd_pcm_start(alsa_indev[iodev].a_handle); fprintf(stderr, "restart alsa input\n"); if (res2 < 0) fprintf(stderr, "alsa xrun recovery apparently failed\n"); } snd_pcm_status(alsa_indev[iodev].a_handle, alsa_status); if (snd_pcm_status_get_avail(alsa_status) < transfersize) return (SENDDACS_NO); } for (iodev = 0; iodev < alsa_noutdev; iodev++) { result = snd_pcm_state(alsa_outdev[iodev].a_handle); if (result == SND_PCM_STATE_XRUN) { int res2 = snd_pcm_start(alsa_outdev[iodev].a_handle); fprintf(stderr, "restart alsa output\n"); if (res2 < 0) fprintf(stderr, "alsa xrun recovery apparently failed\n"); } snd_pcm_status(alsa_outdev[iodev].a_handle, alsa_status); if (snd_pcm_status_get_avail(alsa_status) < transfersize) return (SENDDACS_NO); } #ifdef DEBUG_ALSA_XFER post("xfer %d", transfersize); #endif /* do output */ for (iodev = 0, fp1 = STUFF->st_soundout, ch = 0; iodev < alsa_noutdev; iodev++) { int thisdevchans = alsa_outdev[iodev].a_channels; int chans = (chansouttogo < thisdevchans ? chansouttogo : thisdevchans); chansouttogo -= chans; if (alsa_outdev[iodev].a_sampwidth == 4) { for (i = 0; i < chans; i++, ch++, fp1 += DEFDACBLKSIZE) for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; j += thisdevchans, fp2++) { float s1 = *fp2 * INT32_MAX; ((t_alsa_sample32 *)alsa_snd_buf)[j] = CLIP32(s1); } for (; i < thisdevchans; i++, ch++) for (j = i, k = DEFDACBLKSIZE; k--; j += thisdevchans) ((t_alsa_sample32 *)alsa_snd_buf)[j] = 0; } else if (alsa_outdev[iodev].a_sampwidth == 3) { for (i = 0; i < chans; i++, ch++, fp1 += DEFDACBLKSIZE) for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; j += thisdevchans, fp2++) { int s = *fp2 * 8388352.; if (s > 8388351) s = 8388351; else if (s < -8388351) s = -8388351; #if BYTE_ORDER == LITTLE_ENDIAN ((char *)(alsa_snd_buf))[3*j] = (s & 255); ((char *)(alsa_snd_buf))[3*j+1] = ((s>>8) & 255); ((char *)(alsa_snd_buf))[3*j+2] = ((s>>16) & 255); #else fprintf(stderr, "big endian 24-bit not supported"); #endif } for (; i < thisdevchans; i++, ch++) for (j = i, k = DEFDACBLKSIZE; k--; j += thisdevchans) ((char *)(alsa_snd_buf))[3*j] = ((char *)(alsa_snd_buf))[3*j+1] = ((char *)(alsa_snd_buf))[3*j+2] = 0; } else /* 16 bit samples */ { for (i = 0; i < chans; i++, ch++, fp1 += DEFDACBLKSIZE) for (j = ch, k = DEFDACBLKSIZE, fp2 = fp1; k--; j += thisdevchans, fp2++) { int s = *fp2 * 32767.; if (s > 32767) s = 32767; else if (s < -32767) s = -32767; ((t_alsa_sample16 *)alsa_snd_buf)[j] = s; } for (; i < thisdevchans; i++, ch++) for (j = ch, k = DEFDACBLKSIZE; k--; j += thisdevchans) ((t_alsa_sample16 *)alsa_snd_buf)[j] = 0; } result = snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, transfersize); if (result != (int)transfersize) { #ifdef DEBUG_ALSA_XFER if (result >= 0 || errno == EAGAIN) post("ALSA: write returned %d of %d\n", result, transfersize); else post("ALSA: write: %s\n", snd_strerror(errno)); #endif sys_log_error(ERR_DATALATE); if (result == -EPIPE) { result = snd_pcm_prepare(alsa_indev[iodev].a_handle); if (result < 0) fprintf(stderr, "read reset error %d\n", result); } else fprintf(stderr, "read other error %d\n", result); resync = 1; } /* zero out the output buffer */ memset(STUFF->st_soundout, 0, DEFDACBLKSIZE * sizeof(*STUFF->st_soundout) * STUFF->st_outchannels); if (sys_getrealtime() - timenow > 0.002) { #ifdef DEBUG_ALSA_XFER post("output %d took %d msec\n", callno, (int)(1000 * (timenow - timelast))), fflush(stderr); #endif timenow = sys_getrealtime(); sys_log_error(ERR_DACSLEPT); } }
void ALSAInput::Init() { snd_pcm_hw_params_t *alsa_hw_params = NULL; try { // allocate parameter structure if(snd_pcm_hw_params_malloc(&alsa_hw_params) < 0) { throw std::bad_alloc(); } // open PCM device if(snd_pcm_open(&m_alsa_pcm, m_device_name.toAscii().constData(), SND_PCM_STREAM_CAPTURE, 0) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't open PCM device!")); throw ALSAException(); } if(snd_pcm_hw_params_any(m_alsa_pcm, alsa_hw_params) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't get PCM hardware parameters!")); throw ALSAException(); } // set access type if(snd_pcm_hw_params_set_access(m_alsa_pcm, alsa_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set access type!")); throw ALSAException(); } // set sample format if(snd_pcm_hw_params_set_format(m_alsa_pcm, alsa_hw_params, SND_PCM_FORMAT_S16_LE) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set sample format!")); throw ALSAException(); } // set sample rate unsigned int rate = m_sample_rate; if(snd_pcm_hw_params_set_rate_near(m_alsa_pcm, alsa_hw_params, &rate, NULL) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set sample rate!")); throw ALSAException(); } if(rate != m_sample_rate) { Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Sample rate %1 is not supported, using %2 instead. " "This could be a problem if the difference is large.") .arg(m_sample_rate).arg(rate)); //TODO// enable once resampling is ready //m_sample_rate = rate; } // set channel count if(snd_pcm_hw_params_set_channels(m_alsa_pcm, alsa_hw_params, m_channels) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set channel count!")); throw ALSAException(); } // set period count unsigned int periods = m_alsa_periods; if(snd_pcm_hw_params_set_periods_near(m_alsa_pcm, alsa_hw_params, &periods, NULL) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set period count!")); throw ALSAException(); } if(periods != m_alsa_periods) { Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Period count %1 is not supported, using %2 instead. " "This is not a problem.") .arg(m_alsa_periods).arg(periods)); m_alsa_periods = periods; } // set period size snd_pcm_uframes_t period_size = m_alsa_period_size; if(snd_pcm_hw_params_set_period_size_near(m_alsa_pcm, alsa_hw_params, &period_size, NULL) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set period size!")); throw ALSAException(); } if(period_size != m_alsa_period_size) { Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Period size %1 is not supported, using %2 instead. " "This is not a problem.") .arg(m_alsa_period_size).arg(period_size)); m_alsa_period_size = period_size; } // apply parameters if(snd_pcm_hw_params(m_alsa_pcm, alsa_hw_params) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't apply PCM hardware parameters!")); throw ALSAException(); } // free parameter structure snd_pcm_hw_params_free(alsa_hw_params); alsa_hw_params = NULL; } catch(...) { if(alsa_hw_params != NULL) { snd_pcm_hw_params_free(alsa_hw_params); alsa_hw_params = NULL; } throw; } // start PCM device if(snd_pcm_start(m_alsa_pcm) < 0) { Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't start PCM device!")); throw ALSAException(); } // start input thread m_should_stop = false; m_error_occurred = false; m_thread = std::thread(&ALSAInput::InputThread, this); }
/*---------------------------------------------------------------- TIesrFA_ALSA_thread This function is the thread function that reads audio sample data from the audio channel and puts the data into the circular buffers. --------------------------------*/ void* TIesrFA_ALSA_thread( void* aArg ) { int rtnval; int pcmStarted = FALSE; /* The argument to the thread is the TIesrFA instance */ TIesrFA_t * const aTIesrFAInstance = (TIesrFA_t * const) aArg; /* TIesrFA_ALSA specific data */ TIesrFA_ALSA_t * const ALSAData = (TIesrFA_ALSA_t * const) aTIesrFAInstance->impl_data; /* Start the capture of audio data by the audio channel. If it failed to start, then set the read_data flag to zero, indicating failure, and the thread will terminate. */ rtnval = snd_pcm_start( ALSAData->alsa_handle ); if( rtnval < 0 ) ALSAData->read_data = FALSE; else pcmStarted = TRUE; /* Notify main thread that TIesrFA thread has attempted to start the PCM. Success or failure is set in the read_data value. If failed, then the thread will terminate immediately. */ sem_post( &( ALSAData->start_semaphore ) ); /* Loop reading sample data until requested to stop */ while( ALSAData->read_data == TRUE ) { snd_pcm_sframes_t numSamples; /* Wait for audio data to become available. If no data in one second, then something bad has happened. */ rtnval = snd_pcm_wait( ALSAData->alsa_handle, 1000 ); if( rtnval == 0 ) { /* Timeout indicating some bad failure. */ continue; } /* PCM has indicated data is available. Get amount of data available */ numSamples = snd_pcm_avail_update( ALSAData->alsa_handle ); if( numSamples < 0 ) { /* Assume buffer overflow condition */ ALSAData->buffer_overflow = 1; /* Try to recover */ snd_pcm_prepare( ALSAData->alsa_handle ); continue; } /* Determine number of samples to read. Must not exceed buffer size. */ numSamples = numSamples > ALSAData->read_samples ? ALSAData->read_samples : numSamples; /* Read the samples from the PCM */ numSamples = snd_pcm_readi( ALSAData->alsa_handle, ALSAData->read_buffer, numSamples ); /* Transfer samples to circular frame buffers, or handle error on read. */ if( ALSAData->read_data == TRUE ) { if( numSamples > 0 ) { int numBytesRead = numSamples * ALSAData->sample_size; TIesrFA_ALSA_fillframes( aTIesrFAInstance, numBytesRead ); } else if( numSamples == -EPIPE ) { ALSAData->buffer_overflow = 1; snd_pcm_prepare( ALSAData->alsa_handle ); continue; } else { /* Some other failure */ continue; } } } /* The flag has been set to stop processing audio data, so terminate the thread */ if( pcmStarted ) snd_pcm_drop( ALSAData->alsa_handle ); return(void*) TIesrFA_ALSAErrNone; }
int main(int argc, char *argv[]) { int err; struct sniffer_state sts; sts.pcm_name = strdup("plughw:0,0"); sts.stream = SND_PCM_STREAM_PLAYBACK; sts.format = SND_PCM_FORMAT_A_LAW; sts.rate = 8000; // sts.exact_rate; sts.periods = 2; sts.buffer_time = 25000; sts.period_time = 12500; snd_pcm_hw_params_alloca(&sts.hwparams); if (snd_pcm_open(&sts.pcm, sts.pcm_name, sts.stream, 0) < 0) { fprintf(stderr, "Error opening PCM device %s\n", sts.pcm_name); return(-1); } if (snd_pcm_hw_params_any(sts.pcm, sts.hwparams) < 0) { fprintf(stderr, "Can not configure this PCM device.\n"); return(-1); } if (snd_pcm_hw_params_set_access(sts.pcm, sts.hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) < 0) { fprintf(stderr, "Error setting access.\n"); return(-1); } if (snd_pcm_hw_params_set_format(sts.pcm, sts.hwparams, sts.format) < 0) { fprintf(stderr, "Error setting format.\n"); return(-1); } sts.exact_rate = sts.rate; if (snd_pcm_hw_params_set_rate_near(sts.pcm, sts.hwparams, &sts.exact_rate, 0) < 0) { fprintf(stderr, "Error setting rate.\n"); return(-1); } printf("rate: %d\n", sts.exact_rate); if (sts.rate != sts.exact_rate) { fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n" "==> Using %d Hz instead.\n", sts.rate, sts.exact_rate); } if (snd_pcm_hw_params_set_channels(sts.pcm, sts.hwparams, 1) < 0) { fprintf(stderr, "Error setting channels.\n"); return(-1); } if (snd_pcm_hw_params_set_periods(sts.pcm, sts.hwparams, sts.periods, 0) < 0) { fprintf(stderr, "Error setting periods.\n"); return(-1); } if (snd_pcm_hw_params_set_buffer_time_near(sts.pcm, sts.hwparams, &sts.buffer_time, &sts.dir) < 0) { fprintf(stderr, "Error setting buffersize.\n"); return(-1); } printf("buffer_time set to %d\n", sts.buffer_time); err = snd_pcm_hw_params_get_period_size(sts.hwparams, &sts.period_size, &sts.dir); if (err < 0) { printf("Unable to get period size for playback: %s\n", snd_strerror(err)); return err; } printf("period_size = %d\n", (int)sts.period_size); if (snd_pcm_hw_params(sts.pcm, sts.hwparams) < 0) { fprintf(stderr, "Error setting HW params.\n"); return(-1); } setvbuf(stdout, (char *)NULL, _IONBF, 0); int router_control_fd = open("/dev/visdn/router-control", O_RDWR); if (router_control_fd < 0) { perror("Unable to open router-control"); return 1; } int fd; fd = open("/dev/visdn/streamport", O_RDWR); if (fd < 0) { perror("cannot open /dev/visdn/streamport"); return 1; } struct vsp_ctl vsp_ctl; if (ioctl(fd, VISDN_SP_GET_NODEID, (caddr_t)&vsp_ctl) < 0) { perror("ioctl(VISDN_SP_GET_NODEID)"); return 1; } char node_id[80]; snprintf(node_id, sizeof(node_id), "/sys/%s", vsp_ctl.node_id); struct visdn_connect vc; memset(&vc, 0, sizeof(vc)); strncpy(vc.from_endpoint, argv[1], sizeof(vc.from_endpoint)); strncpy(vc.to_endpoint, node_id, sizeof(vc.to_endpoint)); printf("Connect: %s => %s\n", vc.from_endpoint, vc.to_endpoint); if (ioctl(router_control_fd, VISDN_IOC_CONNECT, (caddr_t) &vc) < 0) { perror("ioctl(VISDN_CONNECT, br=>sp)"); return 1; } int pipeline_id = vc.pipeline_id; memset(&vc, 0, sizeof(vc)); vc.pipeline_id = pipeline_id; if (ioctl(router_control_fd, VISDN_IOC_PIPELINE_OPEN, (caddr_t)&vc) < 0) { perror("ioctl(VISDN_PIPELINE_OPEN, br=>sp)"); return 1; } memset(&vc, 0, sizeof(vc)); vc.pipeline_id = pipeline_id; if (ioctl(router_control_fd, VISDN_IOC_PIPELINE_START, (caddr_t)&vc) < 0) { perror("ioctl(VISDN_PIPELINE_START, br=>sp)"); return 1; } //double phase = 0; const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t avail, commitres; snd_pcm_state_t state; int first = 1; while (1) { state = snd_pcm_state(sts.pcm); if (state == SND_PCM_STATE_XRUN) { err = xrun_recovery(sts.pcm, -EPIPE); if (err < 0) { printf("XRUN recovery failed: %s\n", snd_strerror(err)); return err; } first = 1; } else if (state == SND_PCM_STATE_SUSPENDED) { err = xrun_recovery(sts.pcm, -ESTRPIPE); if (err < 0) { printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); return err; } } avail = snd_pcm_avail_update(sts.pcm); if (avail < 0) { err = xrun_recovery(sts.pcm, avail); if (err < 0) { printf("avail update failed: %s\n", snd_strerror(err)); return err; } first = 1; continue; } if (avail < sts.period_size) { if (first) { first = 0; err = snd_pcm_start(sts.pcm); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } else { err = snd_pcm_wait(sts.pcm, -1); if (err < 0) { if ((err = xrun_recovery(sts.pcm, err)) < 0) { printf("snd_pcm_wait error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } } continue; } size = sts.period_size; while (size > 0) { frames = size; err = snd_pcm_mmap_begin(sts.pcm, &my_areas, &offset, &frames); if (err < 0) { if ((err = xrun_recovery(sts.pcm, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } int r = read(fd, my_areas[0].addr + offset, frames); printf("%d %d %d: ", (int)offset, (int)frames, r); int i; for (i=0; i<r; i++) printf("%02x", *(__u8 *)(my_areas[0].addr + i)); printf("\n"); commitres = snd_pcm_mmap_commit(sts.pcm, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = xrun_recovery(sts.pcm, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } size -= frames; } } return 0; }
bool AlsaSource::PreparePcm (snd_pcm_sframes_t *avail) { int err = 0; bool closed = false; snd_pcm_state_t state; mutex.Lock (); if (initialized) { state = snd_pcm_state (pcm); } else { LOG_ALSA ("AlsaSource::PreparePcm (): pcm has been closed.\n"); closed = true; } mutex.Unlock (); if (closed) return false; switch (state) { case SND_PCM_STATE_XRUN: LOG_ALSA ("AlsaSource::PreparePcm (): SND_PCM_STATE_XRUN.\n"); if (!XrunRecovery (-EPIPE)) return false; started = false; break; case SND_PCM_STATE_SUSPENDED: if (!XrunRecovery (-ESTRPIPE)) return false; break; case SND_PCM_STATE_SETUP: if (!XrunRecovery (-EPIPE)) return false; started = false; break; case SND_PCM_STATE_RUNNING: started = true; // We might have gotten started automatically after writing a certain number of samples. case SND_PCM_STATE_PREPARED: break; case SND_PCM_STATE_PAUSED: case SND_PCM_STATE_DRAINING: default: LOG_ALSA ("AlsaSource::PreparePcm (): state: %s (prepare failed)\n", snd_pcm_state_name (state)); return false; } err = 0; mutex.Lock (); if (initialized) { *avail = snd_pcm_avail_update (pcm); } else { closed = true; } mutex.Unlock (); if (closed) return false; if (*avail < 0) { if (!XrunRecovery (*avail)) return false; started = false; return false; } if ((snd_pcm_uframes_t) *avail < period_size) { if (!started) { LOG_ALSA ("AlsaSource::PreparePcm (): starting pcm (period size: %li, available: %li)\n", period_size, *avail); mutex.Lock (); if (initialized) { err = snd_pcm_start (pcm); } else { closed = true; } mutex.Unlock (); if (closed) return false; if (err < 0) { LOG_AUDIO ("AlsaPlayer: Could not start pcm: %s\n", snd_strerror (err)); return false; } started = true; } else { return false; } return false; } LOG_ALSA ("AlsaSource::PreparePcm (): Prepared, avail: %li, started: %i\n", *avail, (int) started); return true; }
static void async_direct_callback(snd_async_handler_t *ahandler) { snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); struct sniffer_state *sns = snd_async_handler_get_callback_private(ahandler); const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t avail, commitres; snd_pcm_state_t state; int first = 0, err; while (1) { state = snd_pcm_state(handle); if (state == SND_PCM_STATE_XRUN) { err = xrun_recovery(handle, -EPIPE); if (err < 0) { printf("XRUN recovery failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } else if (state == SND_PCM_STATE_SUSPENDED) { err = xrun_recovery(handle, -ESTRPIPE); if (err < 0) { printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } avail = snd_pcm_avail_update(handle); if (avail < 0) { err = xrun_recovery(handle, avail); if (err < 0) { printf("avail update failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; continue; } if (avail < sns->period_size) { if (first) { first = 0; err = snd_pcm_start(handle); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } else { break; } continue; } size = sns->period_size; while (size > 0) { frames = size; err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); if (err < 0) { if ((err = xrun_recovery(handle, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } printf("Callback %d %d\n", (int)offset, (int)frames); int i; for(i=0; i<frames; i++) *(__u8 *)(my_areas[0].addr + offset + i)=i%64; //generate_sine(my_areas, offset, frames, &sns->phase); commitres = snd_pcm_mmap_commit(handle, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } size -= frames; } } }
static int handle(struct device *dv) { int r; unsigned short revents; struct alsa *alsa = (struct alsa*)dv->local; /* Check input buffer for timecode capture */ r = pcm_revents(&alsa->capture, &revents); if (r < 0) return -1; if (revents & POLLIN) { r = capture(dv); if (r < 0) { if (r == -EPIPE) { fputs("ALSA: capture xrun.\n", stderr); r = snd_pcm_prepare(alsa->capture.pcm); if (r < 0) { alsa_error("prepare", r); return -1; } r = snd_pcm_start(alsa->capture.pcm); if (r < 0) { alsa_error("start", r); return -1; } } else { alsa_error("capture", r); return -1; } } } /* Check the output buffer for playback */ r = pcm_revents(&alsa->playback, &revents); if (r < 0) return -1; if (revents & POLLOUT) { r = playback(dv); if (r < 0) { if (r == -EPIPE) { fputs("ALSA: playback xrun.\n", stderr); r = snd_pcm_prepare(alsa->playback.pcm); if (r < 0) { alsa_error("prepare", r); return -1; } /* The device starts when data is written. POLLOUT * events are generated in prepared state. */ } else { alsa_error("playback", r); return -1; } } } return 0; }
void AudioDriver_ALSA::async_direct_callback(snd_async_handler_t *ahandler) { snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); AudioDriver_ALSA* audioDriver = (AudioDriver_ALSA*) snd_async_handler_get_callback_private(ahandler); const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t avail, commitres; snd_pcm_state_t state; int first = 0, err; while (1) { state = snd_pcm_state(handle); if (state == SND_PCM_STATE_XRUN) { err = snd_pcm_recover(handle, -EPIPE, 0); if (err < 0) { fprintf(stderr, "ALSA: XRUN recovery failed: %s\n", snd_strerror(err)); } first = 1; } else if (state == SND_PCM_STATE_SUSPENDED) { err = snd_pcm_recover(handle, ESTRPIPE, 0); if (err < 0) { fprintf(stderr, "ALSA: SUSPEND recovery failed: %s\n", snd_strerror(err)); } } avail = snd_pcm_avail_update(handle); if (avail < 0) { err = snd_pcm_recover(handle, avail, 0); if (err < 0) { fprintf(stderr, "ALSA: avail update failed: %s\n", snd_strerror(err)); } first = 1; continue; } if (avail < audioDriver->period_size) { if (first) { first = 0; err = snd_pcm_start(handle); if (err < 0) { fprintf(stderr, "ALSA: Start error: %s\n", snd_strerror(err)); } } else { break; } continue; } frames = audioDriver->period_size; err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); if (err < 0) { if ((err = snd_pcm_recover(handle, err, 0)) < 0) { fprintf(stderr, "ALSA: MMAP begin avail error: %s\n", snd_strerror(err)); } first = 1; } if(frames != audioDriver->period_size) fprintf(stderr, "ALSA: Invalid buffer size: %lu (should be %lu), skipping..\n", frames, audioDriver->period_size); // Certain audio drivers will periodically request buffers of less than one period when // soft-resampling (ie, not running at native frequency). Milkytracker can't handle this, // and bad things happen - so best to warn the user and not process. // PS - I've disabled soft-resampling for now (see below) so this shouldn't happen. // PPS - The downside is that if the user has the wrong mixer rate, they will get an error // dialog - hopefully they'll read the message on stderr... else audioDriver->fillAudioWithCompensation(static_cast<char*> (my_areas->addr) + offset*4, frames * 2); commitres = snd_pcm_mmap_commit(handle, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = snd_pcm_recover(handle, commitres >= 0 ? -EPIPE : commitres, 0)) < 0) { fprintf(stderr, "ALSA: MMAP commit error: %s\n", snd_strerror(err)); // What now? // exit(1); } first = 1; } } }
bool QAudioInputPrivate::open() { #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif clockStamp.restart(); timeStamp.restart(); elapsedTimeOffset = 0; int dir; int err = 0; int count=0; unsigned int freakuency=settings.frequency(); if (!settings.isValid()) { qWarning("QAudioOutput: open error, invalid format."); } else if (settings.sampleRate() <= 0) { qWarning("QAudioOutput: open error, invalid sample rate (%d).", settings.sampleRate()); } else { err = -1; } if (err == 0) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit errorChanged(errorState); return false; } QString dev = QString(QLatin1String(m_device.constData())); QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioInput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) if (devices.size() > 0) dev = QLatin1String(devices.first()); else return false; #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(m_device); #else int idx = 0; char *name; QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); while(snd_card_get_name(idx,&name) == 0) { if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } // Step 1: try and open the device while((count < 5) && (err < 0)) { err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); if(err < 0) count++; } if (( err < 0)||(handle == 0)) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_nonblock( handle, 0 ); // Step 2: Set the desired HW parameters. snd_pcm_hw_params_alloca( &hwparams ); bool fatal = false; QString errMessage; unsigned int chunks = 8; err = snd_pcm_hw_params_any( handle, hwparams ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err); } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_access( handle, hwparams, access ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err); } } if ( !fatal ) { err = setFormat(); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err); } } if( err < 0) { qWarning()<<errMessage; errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); period_size = snd_pcm_frames_to_bytes(handle,period_frames); snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); // Step 3: Set the desired SW parameters. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); snd_pcm_sw_params(handle, swparams); // Step 4: Prepare audio if(audioBuffer == 0) audioBuffer = new char[buffer_size]; snd_pcm_prepare( handle ); snd_pcm_start(handle); // Step 5: Setup timer bytesAvailable = checkBytesReady(); if(pullMode) connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); // Step 6: Start audio processing chunks = buffer_size/period_size; timer->start(period_time*chunks/2000); errorState = QAudio::NoError; totalTimeValue = 0; return true; }
void CALSAAudioSource::ProcessAudio(void) { int err; if (m_audioSrcFrameNumber == 0) { // Start the device if ((err = snd_pcm_start(m_pcmHandle)) < 0) { error_message("Couldn't start the PCM device: %s", snd_strerror(err)); } } snd_pcm_status_t *status; snd_pcm_status_alloca(&status); // for efficiency, process 1 second before returning to check for commands for (int pass = 0; pass < m_maxPasses && m_stop_thread == false; pass++) { u_int8_t* pcmFrameBuffer; pcmFrameBuffer = (u_int8_t*)malloc(m_pcmFrameSize); // The alsa frames is not the same as the pcm frames used to feed the encoder // Calculate how many alsa frames is neccesary to read to fill one pcm frame snd_pcm_uframes_t num_frames = m_pcmFrameSize / (m_audioSrcChannels * sizeof(u_int16_t)); // Check how many bytes there is to read in the buffer, it will be used to calculate timestamp snd_pcm_status(m_pcmHandle, status); unsigned long avail_bytes = snd_pcm_status_get_avail(status) * (m_audioSrcChannels * sizeof(u_int16_t)); Timestamp currentTime = GetTimestamp(); Timestamp timestamp; // Read num_frames frames from the PCM device // pointed to by pcm_handle to buffer capdata. // Returns the number of frames actually read. // TODO On certain alsa configurations, e.g. when using dsnoop with low sample rate, the period gets too small. What to do about that? snd_pcm_sframes_t framesRead; if((framesRead = snd_pcm_readi(m_pcmHandle, pcmFrameBuffer, num_frames)) == -EPIPE) { snd_pcm_prepare(m_pcmHandle); // Buffer Overrun. This means the audio buffer is full, and not capturing // we want to make the timestamp based on the previous one // When we hit this case, we start using the m_timestampOverflowArray // This will give us a timestamp for when the array is full. // // In other words, if we have a full audio buffer (ie: it's not loading // any more), we start storing the current timestamp into the array. // This will let us "catch up", and have a somewhat accurate timestamp // when we loop around // // wmay - I'm not convinced that this actually works - if the buffer // cleans up, we'll ignore m_timestampOverflowArray if (m_timestampOverflowArray != NULL && m_timestampOverflowArray[m_timestampOverflowArrayIndex] != 0) { timestamp = m_timestampOverflowArray[m_timestampOverflowArrayIndex]; } else { timestamp = m_prevTimestamp + SrcSamplesToTicks(avail_bytes); } if (m_timestampOverflowArray != NULL) m_timestampOverflowArray[m_timestampOverflowArrayIndex] = currentTime; debug_message("audio buffer full !"); } else { if (framesRead < (snd_pcm_sframes_t) num_frames) { error_message("Bad audio read. Expected %li frames, got %li", num_frames, framesRead); free(pcmFrameBuffer); continue; } // buffer is not full - so, we make the timestamp based on the number // of bytes in the buffer that we read. timestamp = currentTime - SrcSamplesToTicks(SrcBytesToSamples(avail_bytes)); if (m_timestampOverflowArray != NULL) m_timestampOverflowArray[m_timestampOverflowArrayIndex] = 0; } //debug_message("alsa read"); #ifdef DEBUG_TIMESTAMPS debug_message("avail_bytes=%lu t="U64" timestamp="U64" delta="U64, avail_bytes, currentTime, timestamp, timestamp - m_prevTimestamp); #endif m_prevTimestamp = timestamp; if (m_timestampOverflowArray != NULL) { m_timestampOverflowArrayIndex = (m_timestampOverflowArrayIndex + 1) % m_audioMaxBufferFrames; } #ifdef DEBUG_TIMESTAMPS debug_message("pcm forward "U64" %u", timestamp, m_pcmFrameSize); #endif if (m_audioSrcFrameNumber == 0 && m_videoSource != NULL) { m_videoSource->RequestKeyFrame(timestamp); } m_audioSrcFrameNumber++; CMediaFrame *frame = new CMediaFrame(PCMAUDIOFRAME, pcmFrameBuffer, m_pcmFrameSize, timestamp); ForwardFrame(frame); } }
int main(int argc, char *argv[]) { struct option long_option[] = { {"help", 0, NULL, 'h'}, {"pdevice", 1, NULL, 'P'}, {"cdevice", 1, NULL, 'C'}, {"min", 1, NULL, 'm'}, {"max", 1, NULL, 'M'}, {"frames", 1, NULL, 'F'}, {"format", 1, NULL, 'f'}, {"channels", 1, NULL, 'c'}, {"rate", 1, NULL, 'r'}, {"seconds", 1, NULL, 's'}, {"block", 0, NULL, 'b'}, {"time", 1, NULL, 't'}, {"poll", 0, NULL, 'p'}, {"effect", 0, NULL, 'e'}, {NULL, 0, NULL, 0}, }; snd_pcm_t *phandle, *chandle; char *buffer; int err, latency, morehelp; int ok; snd_timestamp_t p_tstamp, c_tstamp; ssize_t r; size_t frames_in, frames_out, in_max; int effect = 0; morehelp = 0; while (1) { int c; if ((c = getopt_long(argc, argv, "hP:C:m:M:F:f:c:r:s:bt:pe", long_option, NULL)) < 0) break; switch (c) { case 'h': morehelp++; break; case 'P': pdevice = strdup(optarg); break; case 'C': cdevice = strdup(optarg); break; case 'm': err = atoi(optarg) / 2; latency_min = err >= 4 ? err : 4; if (latency_max < latency_min) latency_max = latency_min; break; case 'M': err = atoi(optarg) / 2; latency_max = latency_min > err ? latency_min : err; break; case 'f': format = snd_pcm_format_value(optarg); if (format == SND_PCM_FORMAT_UNKNOWN) { printf("Unknown format, setting to default S16_LE\n"); format = SND_PCM_FORMAT_S16_LE; } break; case 'c': err = atoi(optarg); channels = err >= 1 && err < 1024 ? err : 1; break; case 'r': err = atoi(optarg); rate = err >= 4000 && err < 200000 ? err : 44100; break; case 's': err = atoi(optarg); loop_sec = err >= 1 && err <= 100000 ? err : 30; break; case 'b': block = 1; break; case 't': tick_time = atoi(optarg); tick_time = tick_time < 0 ? 0 : tick_time; break; case 'p': use_poll = 1; break; case 'e': effect = 1; break; } } if (morehelp) { help(); return 0; } err = snd_output_stdio_attach(&output, stdout, 0); if (err < 0) { printf("Output failed: %s\n", snd_strerror(err)); return 0; } loop_limit = loop_sec * rate; latency = latency_min - 4; buffer = malloc((latency_max * snd_pcm_format_width(format) / 8) * 2); setscheduler(); printf("Playback device is %s\n", pdevice); printf("Capture device is %s\n", cdevice); printf("Parameters are %iHz, %s, %i channels, %s mode\n", rate, snd_pcm_format_name(format), channels, block ? "blocking" : "non-blocking"); printf("Wanted tick time: %ius, poll mode: %s\n", tick_time, use_poll ? "yes" : "no"); printf("Loop limit is %li frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2); if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); return 0; } if ((err = snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE, block ? 0 : SND_PCM_NONBLOCK)) < 0) { printf("Record open error: %s\n", snd_strerror(err)); return 0; } /* initialize the filter sweep variables */ if (effect) { fs = (float) rate; BW = FILTER_BANDWIDTH; lfo = 0; dlfo = 2.*M_PI*FILTERSWEEP_LFO_FREQ/fs; x[0] = (float*) malloc(channels*sizeof(float)); x[1] = (float*) malloc(channels*sizeof(float)); x[2] = (float*) malloc(channels*sizeof(float)); y[0] = (float*) malloc(channels*sizeof(float)); y[1] = (float*) malloc(channels*sizeof(float)); y[2] = (float*) malloc(channels*sizeof(float)); } while (1) { frames_in = frames_out = 0; if (setparams(phandle, chandle, &latency) < 0) break; showlatency(latency); if (tick_time_ok) printf("Using tick time %ius\n", tick_time_ok); if ((err = snd_pcm_link(chandle, phandle)) < 0) { printf("Streams link error: %s\n", snd_strerror(err)); exit(0); } if (snd_pcm_format_set_silence(format, buffer, latency*channels) < 0) { fprintf(stderr, "silence error\n"); break; } if (writebuf(phandle, buffer, latency, &frames_out) < 0) { fprintf(stderr, "write error\n"); break; } if (writebuf(phandle, buffer, latency, &frames_out) < 0) { fprintf(stderr, "write error\n"); break; } if ((err = snd_pcm_start(chandle)) < 0) { printf("Go error: %s\n", snd_strerror(err)); exit(0); } gettimestamp(phandle, &p_tstamp); gettimestamp(chandle, &c_tstamp); #if 0 printf("Playback:\n"); showstat(phandle, frames_out); printf("Capture:\n"); showstat(chandle, frames_in); #endif ok = 1; in_max = 0; while (ok && frames_in < loop_limit) { if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); } if ((r = readbuf(chandle, buffer, latency, &frames_in, &in_max)) < 0) ok = 0; else { if (effect) applyeffect(buffer,r); if (writebuf(phandle, buffer, r, &frames_out) < 0) ok = 0; } } if (ok) printf("Success\n"); else printf("Failure\n"); printf("Playback:\n"); showstat(phandle, frames_out); printf("Capture:\n"); showstat(chandle, frames_in); showinmax(in_max); if (p_tstamp.tv_sec == p_tstamp.tv_sec && p_tstamp.tv_usec == c_tstamp.tv_usec) printf("Hardware sync\n"); snd_pcm_drop(chandle); snd_pcm_nonblock(phandle, 0); snd_pcm_drain(phandle); snd_pcm_nonblock(phandle, !block ? 1 : 0); if (ok) { #if 1 printf("Playback time = %li.%i, Record time = %li.%i, diff = %li\n", p_tstamp.tv_sec, (int)p_tstamp.tv_usec, c_tstamp.tv_sec, (int)c_tstamp.tv_usec, timediff(p_tstamp, c_tstamp)); #endif break; } snd_pcm_unlink(chandle); snd_pcm_hw_free(phandle); snd_pcm_hw_free(chandle); } snd_pcm_close(phandle); snd_pcm_close(chandle); return 0; }
static ALuint ALSAProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; alsa_data *data = (alsa_data*)pDevice->ExtraData; const snd_pcm_channel_area_t *areas = NULL; snd_pcm_uframes_t update_size, num_updates; snd_pcm_sframes_t avail, commitres; snd_pcm_uframes_t offset, frames; char *WritePtr; int err; SetRTPriority(); update_size = pDevice->UpdateSize; num_updates = pDevice->NumUpdates; while(!data->killNow) { int state = verify_state(data->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); aluHandleDisconnect(pDevice); break; } avail = snd_pcm_avail_update(data->pcmHandle); if(avail < 0) { ERR("available update failed: %s\n", snd_strerror(avail)); continue; } if((snd_pcm_uframes_t)avail > update_size*(num_updates+1)) { WARN("available samples exceeds the buffer size\n"); snd_pcm_reset(data->pcmHandle); continue; } // make sure there's frames to process if((snd_pcm_uframes_t)avail < update_size) { if(state != SND_PCM_STATE_RUNNING) { err = snd_pcm_start(data->pcmHandle); if(err < 0) { ERR("start failed: %s\n", snd_strerror(err)); continue; } } if(snd_pcm_wait(data->pcmHandle, 1000) == 0) ERR("Wait timeout... buffer size too low?\n"); continue; } avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop while(avail > 0) { frames = avail; err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); if(err < 0) { ERR("mmap begin error: %s\n", snd_strerror(err)); break; } WritePtr = (char*)areas->addr + (offset * areas->step / 8); aluMixData(pDevice, WritePtr, frames); commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); if(commitres < 0 || (commitres-frames) != 0) { ERR("mmap commit error: %s\n", snd_strerror(commitres >= 0 ? -EPIPE : commitres)); break; } avail -= frames; } } return 0; }