sintonia/library/ecasound-2.7.2/libecasound/plugins/audioio_alsa.cpp

704 lines
21 KiB
C++

// ------------------------------------------------------------------------
// audioio-alsa.cpp: ALSA 0.9.x PCM input and output.
// Copyright (C) 1999-2004,2008 Kai Vehmanen
// Copyright (C) 2001,2002 Jeremy Hall
//
// Attributes:
// eca-style-version: 3
//
// References:
// http://alsa-project.org/
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ------------------------------------------------------------------------
#include <string>
#include <cstring>
#include <cstdio>
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
#include <alsa/version.h>
#define MY_SND_LIB_VERSION(maj,min,sub) \
((maj<<16)| (min<<8)| sub)
/* error if alsa-lib older than 0.9.0, use old API if 0.9.0->0.9.8,
otherwise do nothing */
#if SND_LIB_MAJOR < 1 && SND_LIB_MINOR == 9
#if SND_LIB_SUBMINOR > 0 || SND_LIB_EXTRAVER >= 100004
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#else
#error "Unable to compile ALSA-support. Alsa-lib version 0.9.0rc4 or newer is required!"
#endif
#endif
#include <alsa/asoundlib.h>
#include <kvu_dbc.h>
#include <kvu_message_item.h>
#include <kvu_numtostr.h>
#include <kvu_utils.h>
#include "samplebuffer.h"
#include "audioio-device.h"
#include "audioio_alsa.h"
#include "eca-version.h"
#include "eca-error.h"
#include "eca-logger.h"
using std::cerr;
using std::endl;
#ifndef timersub
#define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
#endif
const string AUDIO_IO_ALSA_PCM::default_pcm_device_rep = "default";
AUDIO_IO_ALSA_PCM::AUDIO_IO_ALSA_PCM (int card,
int device,
int subdevice)
: AUDIO_IO_DEVICE()
{
// ECA_LOG_MSG(ECA_LOGGER::system_objects, "construct");
card_number_rep = card;
device_number_rep = device;
subdevice_number_rep = subdevice;
trigger_request_rep = false;
overruns_rep = underruns_rep = 0;
nbufs_repp = 0;
allocate_structs();
}
AUDIO_IO_ALSA_PCM::~AUDIO_IO_ALSA_PCM(void)
{
if (is_open() == true && is_running()) stop();
if (is_open() == true) {
close();
}
if (io_mode() != io_read) {
if (underruns_rep != 0) {
cerr << "WARNING! While writing to ALSA-pcm device ";
cerr << "C" << card_number_rep << "D" << device_number_rep;
cerr << ", there were " << underruns_rep << " underruns.\n";
}
}
else {
if (overruns_rep != 0) {
cerr << "WARNING! While reading from ALSA-pcm device ";
cerr << "C" << card_number_rep << "D" << device_number_rep;
cerr << ", there were " << overruns_rep << " overruns.\n";
}
}
if (nbufs_repp != 0)
delete nbufs_repp;
deallocate_structs();
}
AUDIO_IO_ALSA_PCM* AUDIO_IO_ALSA_PCM::clone(void) const
{
AUDIO_IO_ALSA_PCM* target = new AUDIO_IO_ALSA_PCM();
for(int n = 0; n < number_of_params(); n++) {
target->set_parameter(n + 1, get_parameter(n + 1));
}
return target;
}
void AUDIO_IO_ALSA_PCM::allocate_structs(void)
{
int err = snd_pcm_hw_params_malloc(&pcm_hw_params_repp);
DBC_CHECK(!err);
err = snd_pcm_sw_params_malloc(&pcm_sw_params_repp);
DBC_CHECK(!err);
}
void AUDIO_IO_ALSA_PCM::deallocate_structs(void)
{
snd_pcm_hw_params_free(pcm_hw_params_repp);
snd_pcm_sw_params_free(pcm_sw_params_repp);
}
void AUDIO_IO_ALSA_PCM::open_device(void)
{
ECA_LOG_MSG(ECA_LOGGER::system_objects, "open");
// -------------------------------------------------------------------
// Device name initialization
string device_name = pcm_device_name();
// -------------------------------------------------------------------
// Open devices
int err;
if (io_mode() == io_read) {
pcm_stream_rep = SND_PCM_STREAM_CAPTURE;
err = snd_pcm_open(&audio_fd_repp,
(char*)device_name.c_str(),
pcm_stream_rep,
SND_PCM_NONBLOCK);
if (err < 0) {
throw(SETUP_ERROR(SETUP_ERROR::io_mode, "AUDIOIO-ALSA: Unable to open ALSA--device for capture; error: " +
string(snd_strerror(err))));
}
}
else if (io_mode() == io_write) {
pcm_stream_rep = SND_PCM_STREAM_PLAYBACK;
err = snd_pcm_open(&audio_fd_repp,
(char*)device_name.c_str(),
pcm_stream_rep,
SND_PCM_NONBLOCK);
if (err < 0) {
throw(SETUP_ERROR(SETUP_ERROR::io_mode, "AUDIOIO-ALSA: Unable to open ALSA-device for playback; error: " +
string(snd_strerror(err))));
}
}
else if (io_mode() == io_readwrite) {
throw(SETUP_ERROR(SETUP_ERROR::io_mode, "AUDIOIO-ALSA: Simultaneous input/output not supported."));
}
// -------------------------------------------------------------------
// enables blocking mode
snd_pcm_nonblock(audio_fd_repp, 0);
}
void AUDIO_IO_ALSA_PCM::set_audio_format_params(void)
{
ECA_LOG_MSG(ECA_LOGGER::system_objects, "set_audio_format_params");
format_rep = SND_PCM_FORMAT_LAST;
switch(sample_format())
{
case ECA_AUDIO_FORMAT::sfmt_u8: { format_rep = SND_PCM_FORMAT_U8; break; }
case ECA_AUDIO_FORMAT::sfmt_s8: { format_rep = SND_PCM_FORMAT_S8; break; }
case ECA_AUDIO_FORMAT::sfmt_s16_le: { format_rep = SND_PCM_FORMAT_S16_LE; break; }
case ECA_AUDIO_FORMAT::sfmt_s16_be: { format_rep = SND_PCM_FORMAT_S16_BE; break; }
case ECA_AUDIO_FORMAT::sfmt_s24_le: { format_rep = SND_PCM_FORMAT_S24_3LE; break; }
case ECA_AUDIO_FORMAT::sfmt_s24_be: { format_rep = SND_PCM_FORMAT_S24_3BE; break; }
case ECA_AUDIO_FORMAT::sfmt_s32_le: { format_rep = SND_PCM_FORMAT_S32_LE; break; }
case ECA_AUDIO_FORMAT::sfmt_s32_be: { format_rep = SND_PCM_FORMAT_S32_BE; break; }
default:
{
throw(SETUP_ERROR(SETUP_ERROR::sample_format, "AUDIOIO-ALSA: Error when setting audio format not supported (1)"));
}
}
}
void AUDIO_IO_ALSA_PCM::print_pcm_info(void)
{
}
void AUDIO_IO_ALSA_PCM::fill_and_set_hw_params(void)
{
ECA_LOG_MSG(ECA_LOGGER::system_objects, "fill_and_set_hw_params");
/* 1. create one param combination */
int err = snd_pcm_hw_params_any(audio_fd_repp, pcm_hw_params_repp);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up hwparams/any: " + string(snd_strerror(err))));
/* 2. set interleaving mode */
if (interleaved_channels() == true)
ECA_LOG_MSG(ECA_LOGGER::user_objects, "Using interleaved stream format.");
else
ECA_LOG_MSG(ECA_LOGGER::user_objects, "Using noninterleaved stream format.");
if (interleaved_channels() == true)
err = snd_pcm_hw_params_set_access(audio_fd_repp, pcm_hw_params_repp,
SND_PCM_ACCESS_RW_INTERLEAVED);
else
err = snd_pcm_hw_params_set_access(audio_fd_repp, pcm_hw_params_repp,
SND_PCM_ACCESS_RW_NONINTERLEAVED);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up hwparams/access: " + string(snd_strerror(err))));
/* 3. set sample format */
err = snd_pcm_hw_params_set_format(audio_fd_repp,
pcm_hw_params_repp,
format_rep);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::sample_format, "AUDIOIO-ALSA: Audio format not supported."));
/* 4. set channel count */
err = snd_pcm_hw_params_set_channels(audio_fd_repp,
pcm_hw_params_repp,
channels());
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::channels, "AUDIOIO-ALSA: Channel count " +
kvu_numtostr(channels()) + " is out of range!"));
/* 5. set sampling rate */
unsigned int uivalue = samples_per_second();
err = snd_pcm_hw_params_set_rate_near(audio_fd_repp,
pcm_hw_params_repp,
&uivalue,
0);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::sample_rate, "AUDIOIO-ALSA: Sample rate " +
kvu_numtostr(samples_per_second()) + " is out of range!"));
/* 6. create buffers for noninterleaved i/o */
if (interleaved_channels() != true) {
if (nbufs_repp == 0)
nbufs_repp = new unsigned char* [channels()];
}
snd_pcm_uframes_t fvalue = buffersize();
/* 7. sets period size (period = one fragment) */
err = snd_pcm_hw_params_set_period_size_near(audio_fd_repp,
pcm_hw_params_repp,
&fvalue,
0);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::buffersize, "AUDIOIO-ALSA: buffersize " +
kvu_numtostr(buffersize()) + " is out of range!"));
/* 8. sets buffer size */
if (max_buffers() == true) {
snd_pcm_uframes_t bufferreq = buffersize() * 1024;
err = snd_pcm_hw_params_set_buffer_size_near(audio_fd_repp,
pcm_hw_params_repp,
&bufferreq);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected,
"AUDIOIO-ALSA: Error when setting up hwparams/btime (1): " + string(snd_strerror(err))));
}
else {
snd_pcm_uframes_t bufferreq = buffersize() * 3;
err = snd_pcm_hw_params_set_buffer_size_near(audio_fd_repp,
pcm_hw_params_repp,
&bufferreq);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected,
"AUDIOIO-ALSA: Error when setting up hwparams/btime (2): " + string(snd_strerror(err))));
}
/* 9. print debug information */
snd_pcm_hw_params_get_period_time(pcm_hw_params_repp, &uivalue, 0);
ECA_LOG_MSG(ECA_LOGGER::system_objects, "period time set to " + kvu_numtostr(uivalue) + " usecs.");
snd_pcm_hw_params_get_period_size(pcm_hw_params_repp, &period_size_rep, 0);
ECA_LOG_MSG(ECA_LOGGER::system_objects, "period time set to " + kvu_numtostr(period_size_rep) + " frames.");
if (period_size_rep != static_cast<unsigned int>(buffersize())) {
ECA_LOG_MSG(ECA_LOGGER::info,
"Warning! Period-size differs from current client buffersize.");
}
snd_pcm_hw_params_get_buffer_time(pcm_hw_params_repp, &uivalue, 0);
ECA_LOG_MSG(ECA_LOGGER::system_objects, "buffer time set to " + kvu_numtostr(uivalue) + " usecs.");
snd_pcm_hw_params_get_buffer_size(pcm_hw_params_repp, &buffer_size_rep);
ECA_LOG_MSG(ECA_LOGGER::system_objects, "buffer time set to " + kvu_numtostr(buffer_size_rep) + " frames.");
ECA_LOG_MSG(ECA_LOGGER::system_objects, "total latency is " + kvu_numtostr(latency()) + " frames.");
/* 9. all set, now active hw params */
err = snd_pcm_hw_params(audio_fd_repp, pcm_hw_params_repp);
if (err < 0) {
throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up hwparams: " + string(snd_strerror(err))));
}
}
void AUDIO_IO_ALSA_PCM::fill_and_set_sw_params(void)
{
ECA_LOG_MSG(ECA_LOGGER::system_objects, "fill_and_set_sw_params");
/* 1. get current params */
snd_pcm_sw_params_current(audio_fd_repp, pcm_sw_params_repp);
/* 2. set start threshold (should be big enough so that processing
won't start until a explicit snd_pcm_start() is issued */
int err = snd_pcm_sw_params_set_start_threshold(audio_fd_repp,
pcm_sw_params_repp,
buffer_size_rep * 2);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up pcm_sw_params/start_threshold: " + string(snd_strerror(err))));
#if SND_LIB_VERSION <= MY_SND_LIB_VERSION(1,0,15)
/* note: deprecated in alsa-lib-1.0.16 (2008/Feb) */
/* 3. set align to one frame (like the OSS-emulation layer) */
err = snd_pcm_sw_params_set_xfer_align(audio_fd_repp,
pcm_sw_params_repp,
1);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up pcm_sw_params_repp/xfer_align: " + string(snd_strerror(err))));
#endif
/* 4. activate params */
err = snd_pcm_sw_params(audio_fd_repp, pcm_sw_params_repp);
if (err < 0) throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-ALSA: Error when setting up pcm_sw_params_repp: " + string(snd_strerror(err))));
}
void AUDIO_IO_ALSA_PCM::open(void) throw(AUDIO_IO::SETUP_ERROR&)
{
open_device();
set_audio_format_params();
fill_and_set_hw_params();
print_pcm_info();
fill_and_set_sw_params();
AUDIO_IO_DEVICE::open();
}
void AUDIO_IO_ALSA_PCM::stop(void)
{
snd_pcm_drop(audio_fd_repp); /* non-blocking */
// snd_pcm_drain(audio_fd_repp); /* blocking */
ECA_LOG_MSG(ECA_LOGGER::user_objects, "stop - " + label() + ".");
AUDIO_IO_DEVICE::stop();
}
void AUDIO_IO_ALSA_PCM::close(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "close - " + label() + ".");
if (is_prepared() == true && is_running() == true) stop();
snd_pcm_close(audio_fd_repp);
AUDIO_IO_DEVICE::close();
}
void AUDIO_IO_ALSA_PCM::prepare(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "prepare - " + label() + ".");
int err = snd_pcm_prepare(audio_fd_repp);
if (err < 0)
ECA_LOG_MSG(ECA_LOGGER::info, "Error when preparing stream: " + string(snd_strerror(err)));
AUDIO_IO_DEVICE::prepare();
}
void AUDIO_IO_ALSA_PCM::start(void)
{
ECA_LOG_MSG(ECA_LOGGER::user_objects, "start - " + label() + ".");
snd_pcm_start(audio_fd_repp);
AUDIO_IO_DEVICE::start();
}
long int AUDIO_IO_ALSA_PCM::read_samples(void* target_buffer,
long int samples)
{
// --
DBC_REQUIRE(samples <= buffersize());
// --
long int realsamples = 0;
if (interleaved_channels() == true) {
realsamples = snd_pcm_readi(audio_fd_repp, target_buffer,
buffersize());
if (realsamples < 0) {
/* Note! ALSA versions <=0.9.1 sometimes return -EIO in xrun-state;
* EPIPE=xrun, ESTRPIPE=xrun) */
if (realsamples == -EPIPE || realsamples == -ESTRPIPE || realsamples == -EIO) {
if (ignore_xruns() == true) {
handle_xrun_capture();
realsamples = snd_pcm_readi(audio_fd_repp, target_buffer,
buffersize());
if (realsamples < 0) realsamples = 0;
}
else {
cerr << "ALSA: Overrun! Stopping operation!" << endl;
stop();
close();
}
}
else {
cerr << "ALSA: Read error (" << realsamples << ")! Stopping operation." << endl;
stop();
close();
}
}
}
else {
unsigned char* ptr_to_channel = reinterpret_cast<unsigned char*>(target_buffer);
for (int channel = 0; channel < channels(); channel++) {
nbufs_repp[channel] = ptr_to_channel;
ptr_to_channel += samples * sample_size();
}
realsamples = snd_pcm_readn(audio_fd_repp, reinterpret_cast<void**>(target_buffer), buffersize());
if (realsamples < 0) {
/* Note! ALSA versions <=0.9.1 sometimes return -EIO in xrun-state;
* EPIPE=xrun, ESTRPIPE=xrun) */
if (realsamples == -EPIPE || realsamples == -ESTRPIPE || realsamples == -EIO) {
if (ignore_xruns() == true) {
handle_xrun_capture();
realsamples = snd_pcm_readn(audio_fd_repp, reinterpret_cast<void**>(target_buffer), buffersize());
if (realsamples < 0) realsamples = 0;
}
else {
cerr << "ALSA: Overrun! Stopping operation!" << endl;
stop();
close();
}
}
else {
cerr << "ALSA: Read error! Stopping operation." << endl;
stop();
close();
}
}
}
return realsamples;
}
void AUDIO_IO_ALSA_PCM::handle_xrun_capture(void)
{
snd_pcm_status_t *status;
snd_pcm_status_alloca(&status);
int res = snd_pcm_status(audio_fd_repp, status);
if (res >= 0) {
snd_pcm_state_t state = snd_pcm_status_get_state(status);
if (state == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp;
gettimeofday(&now, 0);
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
timersub(&now, &tstamp, &diff);
cerr << "WARNING: ALSA recording overrun, some audio samples were lost!"
<< " Break was at least " << kvu_numtostr(diff.tv_sec *
1000 +
diff.tv_usec /
1000.0)
<< " ms long." << endl;
overruns_rep++;
stop();
prepare();
start();
}
else if (state == SND_PCM_STATE_SUSPENDED) {
cerr << "ALSA: Device suspended! Stopping operation!" << endl;
stop();
close();
}
else {
cerr << "ALSA: Unknown device state '"
<< static_cast<int>(state) << "'" << endl;
}
}
else {
ECA_LOG_MSG(ECA_LOGGER::info, "snd_pcm_status() failed!");
}
}
void AUDIO_IO_ALSA_PCM::write_samples(void* target_buffer, long int samples)
{
if (trigger_request_rep == true) {
trigger_request_rep = false;
start();
}
if (interleaved_channels() == true) {
long int count = snd_pcm_writei(audio_fd_repp, target_buffer, samples);
if (count < 0) {
/* Note! ALSA versions <=0.9.1 sometimes return -EIO in xrun-state;
* EPIPE=xrun, ESTRPIPE=xrun) */
DBC_CHECK(count != -EINTR);
if (count == -EPIPE || count == -EIO || count == -ESTRPIPE) {
if (ignore_xruns() == true) {
handle_xrun_playback();
if (snd_pcm_writei(audio_fd_repp, target_buffer, samples) < 0)
cerr << "ALSA: playback xrun handling failed!" << endl;
trigger_request_rep = true;
}
else {
cerr << "ALSA: Overrun! Stopping operation!" << endl;
stop();
close();
}
}
else {
cerr << "ALSA: Write error! Stopping operation (" << count << ")." << endl;
stop();
close();
}
}
}
else {
unsigned char* ptr_to_channel = reinterpret_cast<unsigned char*>(target_buffer);
for (int channel = 0; channel < channels(); channel++) {
nbufs_repp[channel] = ptr_to_channel;
// cerr << "Pointer to channel " << channel << ": " << reinterpret_cast<void*>(nbufs_repp[channel]) << endl;
ptr_to_channel += samples * sample_size();
// cerr << "Advancing pointer count by " << samples * sample_size() << " to " << reinterpret_cast<void*>(ptr_to_channel) << endl;
}
long int count = snd_pcm_writen(audio_fd_repp,
reinterpret_cast<void**>(nbufs_repp),
samples);
if (count < 0) {
/* Note! ALSA versions <=0.9.1 sometimes return -EIO in xrun-state;
* EPIPE=xrun, ESTRPIPE=xrun) */
DBC_CHECK(count != -EINTR);
if (count == -EPIPE || count == -EIO || count == -ESTRPIPE) {
if (ignore_xruns() == true) {
handle_xrun_playback();
snd_pcm_writen(audio_fd_repp,
reinterpret_cast<void**>(nbufs_repp),
samples);
trigger_request_rep = true;
}
else {
cerr << "ALSA: Overrun! Stopping operation!" << endl;
stop();
close();
}
}
else {
cerr << "ALSA: Write error! Stopping operation." << endl;
stop();
close();
}
}
}
}
void AUDIO_IO_ALSA_PCM::handle_xrun_playback(void)
{
snd_pcm_status_t *status;
snd_pcm_status_alloca(&status);
int res = snd_pcm_status(audio_fd_repp, status);
if (res >= 0) {
snd_pcm_state_t state = snd_pcm_status_get_state(status);
if (state == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp;
gettimeofday(&now, 0);
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
timersub(&now, &tstamp, &diff);
cerr << "WARNING: ALSA playback underrun, glitches in audio playback possible!"
<< " Break was at least " << kvu_numtostr(diff.tv_sec *
1000 +
diff.tv_usec /
1000.0)
<< " ms long." << endl;
underruns_rep++;
stop();
prepare();
trigger_request_rep = true;
}
else if (state == SND_PCM_STATE_SUSPENDED) {
cerr << "ALSA: Device suspended! Stopping operation!" << endl;
stop();
close();
}
else {
cerr << "ALSA: Unknown device state '"
<< static_cast<int>(state) << "'" << endl;
}
}
else {
ECA_LOG_MSG(ECA_LOGGER::info, "snd_pcm_status() failed!");
}
}
long int AUDIO_IO_ALSA_PCM::delay(void) const
{
snd_pcm_sframes_t delay = 0;
if (is_running() == true) {
if (snd_pcm_delay(audio_fd_repp, &delay) != 0) {
delay = 0;
}
}
return static_cast<long int>(delay);
}
void AUDIO_IO_ALSA_PCM::set_parameter(int param,
string value)
{
switch (param) {
case 1:
set_label(value);
if (label().find("alsaplugin") != string::npos) {
using_plugin_rep = true;
}
break;
case 2:
card_number_rep = atoi(value.c_str());
break;
case 3:
device_number_rep = atoi(value.c_str());
break;
case 4:
subdevice_number_rep = atoi(value.c_str());
break;
}
if (using_plugin_rep)
pcm_device_name_rep =
string("plughw:") +
kvu_numtostr(card_number_rep) +
"," +
kvu_numtostr(device_number_rep) +
"," +
kvu_numtostr(subdevice_number_rep);
else
pcm_device_name_rep =
string("hw:") +
kvu_numtostr(card_number_rep) +
"," +
kvu_numtostr(device_number_rep) +
"," +
kvu_numtostr(subdevice_number_rep);
}
string AUDIO_IO_ALSA_PCM::get_parameter(int param) const
{
switch (param) {
case 1:
return label();
case 2:
return kvu_numtostr(card_number_rep);
case 3:
return kvu_numtostr(device_number_rep);
case 4:
return kvu_numtostr(subdevice_number_rep);
}
return "";
}
void AUDIO_IO_ALSA_PCM::set_pcm_device_name(const string& n)
{
if (n.size() > 0)
pcm_device_name_rep = n;
else
pcm_device_name_rep = default_pcm_device_rep;
}