#include <alsa/asoundlib.h> #include "MonitorAudioALSA.h" #include "../MonitorLogging.h" MonitorAudioALSA::MonitorAudioALSA(const std::string* name, tSamplerate rate) : MonitorAudio(name, rate) { run = false; } MonitorAudioALSA::~MonitorAudioALSA() { CloseDevice(); } bool MonitorAudioALSA::Start(void *format) { if (InitDevice() < 0) { FILE_LOG(logERROR) << "[ALSA] Error initializing PCM device " << pcm_name; exit(10); } run = true; JThread::Start(); return true; } void MonitorAudioALSA::Stop() { run = false; } int MonitorAudioALSA::InitDevice() { if ((pcm_name.length() == 0) || (pcm_rate == 0)) { FILE_LOG(logERROR) << "[ALSA] InitDevice Argument Error: pcm_name=" << pcm_name << " pcm_rate=" << pcm_rate ; return -1; } int ret; snd_pcm_uframes_t pcm_buffer_size = audio_buffer->SampleLen; snd_pcm_stream_t pcm_stream = SND_PCM_STREAM_CAPTURE; unsigned int periods = 2; int direction = 0; /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); /* Open PCM. The last parameter of this function is the mode. */ /* If this is set to 0, the standard mode is used. Possible */ /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC. */ /* If SND_PCM_NONBLOCK is used, read / write access to the */ /* PCM device will return immediately. If SND_PCM_ASYNC is */ /* specified, SIGIO will be emitted whenever a period has */ /* been completely processed by the soundcard. */ ret = snd_pcm_open(&pcm_handle, pcm_name.c_str(), pcm_stream, 0); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error opening PCM device " << pcm_name << " ret:" << ret << snd_strerror(ret); return -1; } /* Init hwparams with full configuration space */ ret = snd_pcm_hw_params_any(pcm_handle, hwparams); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Can not configure this PCM device " << pcm_name << ". " << ret << "(" << snd_strerror(ret) << ")" ; return -1; } /* Set access type. This can be either */ /* SND_PCM_ACCESS_RW_INTERLEAVED or */ /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */ /* There are also access types for MMAPed */ /* access, but this is beyond the scope */ /* of this introduction. */ ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_NONINTERLEAVED); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting access " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; return -1; } /* Set number of channels */ ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting channels " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; return -1; } /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &pcm_rate, &direction); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting rate " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; return -1; } /* Set sample format */ /* FLOAT LE -1.0 .. 1.0 */ ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_FLOAT_LE); //ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting format " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; return -1; } /* Set buffer size */ ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &pcm_buffer_size); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting buffer size " << pcm_name << ". " << (int)pcm_buffer_size << " - " << ret <<"(" << snd_strerror(ret)<<")"; } /* Set number of periods. Periods used to be called fragments. */ ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, &direction); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting periods " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; } FILE_LOG(logINFO) << "[ALSA] Using pcm_buffer_size=" <<(int)pcm_buffer_size << " periods=" << periods ; /* Apply HW parameter settings to */ /* PCM device and prepare device */ ret = snd_pcm_hw_params(pcm_handle, hwparams); if (ret < 0) { FILE_LOG(logERROR) << "[ALSA] Error setting HW params " << pcm_name << ". " << ret <<"(" << snd_strerror(ret)<<")"; return -1; } return 1; } int MonitorAudioALSA::CloseDevice() { snd_pcm_close(pcm_handle); return 0; } void* MonitorAudioALSA::Thread() { signed int num_samples; JThread::ThreadStarted(); while(run) { num_samples = snd_pcm_readn(pcm_handle, (void**)audio_buffer->Ptrs, audio_buffer->SampleLen); if (num_samples > 0) { audio_buffer->Samples = num_samples; DataFromSoundIn(audio_buffer, m_pOwner); } else if (num_samples == -EPIPE) { int err = snd_pcm_prepare(pcm_handle); if (err < 0) { FILE_LOG(logERROR) << "[ALSA] Can't recovery from underrun, prepare failed: " << snd_strerror(err); } } else if (num_samples < 0) { FILE_LOG(logERROR) << "[ALSA] Read error " << num_samples << "(" << snd_strerror(num_samples) << ")" ; /* Schliessen des Sounddevices und neustarten */ CloseDevice(); sleep(1); if (InitDevice() < 0) { FILE_LOG(logERROR) << "[ALSA] Error initializing PCM device " << pcm_name ; } } } return NULL; } // vim: sw=4 ts=4 cindent