#include #include #include #include #include #include #include "device_alsa.h" #include "debug.h" namespace audiere { static snd_pcm_t* openDevice(const std::string &name, unsigned int &rate, unsigned int &bufsz) { int status = 0; snd_pcm_t* pcm_handle = 0; status = snd_pcm_open(&pcm_handle, name.c_str (), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (status < 0) { ADR_LOG("Coudn't open haredware."); return 0; } snd_pcm_hw_params_t* hwparams; snd_pcm_sw_params_t* swparams; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); status = snd_pcm_hw_params_any(pcm_handle, hwparams); if (status < 0) { ADR_LOG("Coudn't get hardware parameters."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (status < 0) { ADR_LOG("Coudn't set access mode."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE); if (status < 0) { ADR_LOG("Coudn't set pcm format."); snd_pcm_close(pcm_handle); return 0; } unsigned int channels = 2; status = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &channels); if (status < 0) { ADR_LOG("Coudn't set channels."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, NULL); if (status < 0) { ADR_LOG("Coudn't set sample rate."); snd_pcm_close(pcm_handle); return 0; } snd_pcm_uframes_t frame_size = 1024; status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &frame_size, NULL); if (status < 0) { ADR_LOG("Coudn't set buffer size."); snd_pcm_close(pcm_handle); return 0; } unsigned frames = 8; status = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &frames, NULL); if (status < 0) { ADR_LOG("Coudn't set periods."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_hw_params(pcm_handle, hwparams); if (status < 0) { ADR_LOG("Coudn't set hardware parameters."); snd_pcm_close(pcm_handle); return 0; } snd_pcm_uframes_t buffer_size; status = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); if (status < 0) { ADR_LOG("Coudn't get buffer size."); snd_pcm_close(pcm_handle); return 0; } snd_pcm_uframes_t period_size; status = snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL); if (status < 0) { ADR_LOG("Coudn't get period size."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_sw_params_current(pcm_handle, swparams); if (status < 0) { ADR_LOG("Coudn't get software parameters."); snd_pcm_close(pcm_handle); return 0; } snd_pcm_uframes_t boundary; #if SND_LIB_VERSION >= 0x000901 status = snd_pcm_sw_params_get_boundary(swparams, &boundary); if (status < 0) { ADR_LOG("Coudn't get boundary."); snd_pcm_close(pcm_handle); return 0; } #else boundary = 0x7fffffff; #endif status = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size); if (status < 0) { ADR_LOG("Coudn't get hardware parameters."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, buffer_size); if (status < 0) { ADR_LOG("Coudn't set stop threshold."); snd_pcm_close(pcm_handle); return 0; } status = snd_pcm_sw_params(pcm_handle, swparams); if (status < 0) { ADR_LOG("Couldn't set audio device parameters."); snd_pcm_close(pcm_handle); return 0; } bufsz = period_size * channels * 2; return pcm_handle; } ALSAAudioDevice* ALSAAudioDevice::create(const ParameterList& parameters) { std::string devices[] = { "default", "plughw:0,0", "hw:0,0", "" }; std::string default_device = parameters.getValue("device", devices[0].c_str()); // replace '.' with ',' for (size_t i = 0; i < default_device.length(); i++) if (default_device[i] == '.') default_device[i] = ','; if (default_device != devices[0]) devices[0] = default_device; unsigned int i, rate, buffer_size; snd_pcm_t* pcm_handle = 0; for (i = 0; devices[i].length() > 0; i++) { rate = 48000; pcm_handle = openDevice(devices[i], rate, buffer_size); if (!pcm_handle) { rate = 44100; pcm_handle = openDevice(devices[i], rate, buffer_size); } if (pcm_handle) break; } if (!pcm_handle) { ADR_LOG("Coudn't open haredware."); return 0; } return new ALSAAudioDevice(pcm_handle, devices[i], rate, buffer_size); } ALSAAudioDevice::ALSAAudioDevice(snd_pcm_t* pcm_handle, const std::string &name, unsigned int rate, unsigned int buffer_size) : MixerDevice(rate), m_pcm_name(name), m_rate(rate) { m_pcm_handle = pcm_handle; m_buffer_size = buffer_size; m_buffer = new char [buffer_size]; } ALSAAudioDevice::~ALSAAudioDevice() { ADR_GUARD("ALSAAudioDevice::~ALSAAudioDevice"); if (m_pcm_handle) { snd_pcm_drain(m_pcm_handle); snd_pcm_close(m_pcm_handle); } delete [] m_buffer; } void ADR_CALL ALSAAudioDevice::update() { int ret; int sample_len; int sample_left; char* sample_buf; int retry = 0; // reopen a pcm if (!m_pcm_handle) { m_pcm_handle = openDevice(m_pcm_name, m_rate, m_buffer_size); if (!m_pcm_handle) { usleep(100); return; } delete [] m_buffer; m_buffer = new char [m_buffer_size]; } sample_buf = m_buffer; sample_len = m_buffer_size / 4; sample_left = read(sample_len, sample_buf); while (sample_left > 0 && m_pcm_handle) { ret = snd_pcm_writei(m_pcm_handle, sample_buf, sample_left); if (ret == -EAGAIN) { snd_pcm_wait(m_pcm_handle, 1000); if (++retry > 5) { snd_pcm_close(m_pcm_handle); m_pcm_handle = 0; break; } } else if (ret == -ESTRPIPE) { do { snd_pcm_wait(m_pcm_handle, 10); ret = snd_pcm_resume(m_pcm_handle); } while (ret == -EAGAIN); snd_pcm_prepare(m_pcm_handle); } else if (ret == -EPIPE) { snd_pcm_prepare(m_pcm_handle); } if (ret > 0) { sample_buf += ret * 4; sample_left -= ret; } } } const char* ADR_CALL ALSAAudioDevice::getName() { return "alsa"; } }