21 |
***************************************************************************/ |
***************************************************************************/ |
22 |
|
|
23 |
#include "AudioOutputDeviceAlsa.h" |
#include "AudioOutputDeviceAlsa.h" |
24 |
|
#include "AudioOutputDeviceFactory.h" |
25 |
|
|
26 |
namespace LinuxSampler { |
namespace LinuxSampler { |
27 |
|
|
28 |
|
REGISTER_AUDIO_OUTPUT_DRIVER("ALSA",AudioOutputDeviceAlsa); |
29 |
|
|
30 |
/** |
/** |
31 |
* Open and initialize Alsa output device with given parameters. |
* Create and initialize Alsa audio output device with given parameters. |
32 |
* |
* |
33 |
* @param Channels - number of audio channels |
* @param Parameters - optional parameters |
|
* @param Fragments - number of audio fragments |
|
|
* @param FragmentSize - size of each fragment (in sample points) |
|
|
* @param Card - Alsa soundcard ID (optional) |
|
34 |
* @throws AudioOutputException if output device cannot be opened |
* @throws AudioOutputException if output device cannot be opened |
35 |
*/ |
*/ |
36 |
AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(uint Channels, uint Samplerate, uint Fragments, uint FragmentSize, String Card) : AudioOutputDevice(AudioOutputDevice::type_alsa), Thread(true, 1, 0) { |
AudioOutputDeviceAlsa::AudioOutputDeviceAlsa(std::map<String,String> Parameters) : AudioOutputDevice(CreateParameters(Parameters)), Thread(true, 1, 0) { |
37 |
pcm_handle = NULL; |
pcm_handle = NULL; |
38 |
stream = SND_PCM_STREAM_PLAYBACK; |
stream = SND_PCM_STREAM_PLAYBACK; |
39 |
this->uiAlsaChannels = Channels; |
this->uiAlsaChannels = ((DeviceCreationParameterInt*)this->Parameters["CHANNELS"])->ValueAsInt(); |
40 |
this->uiSamplerate = Samplerate; |
this->uiSamplerate = ((DeviceCreationParameterInt*)this->Parameters["SAMPLERATE"])->ValueAsInt(); |
41 |
this->FragmentSize = FragmentSize; |
this->FragmentSize = ((DeviceCreationParameterInt*)this->Parameters["FRAGMENTSIZE"])->ValueAsInt(); |
42 |
|
uint Fragments = ((DeviceCreationParameterInt*)this->Parameters["FRAGMENTS"])->ValueAsInt(); |
43 |
|
String Card = this->Parameters["CARD"]->Value(); |
44 |
|
|
45 |
if (HardwareParametersSupported(Channels, Samplerate, Fragments, FragmentSize)) { |
dmsg(1,("Checking if hw parameters supported...\n")); |
46 |
|
if (HardwareParametersSupported(Card, uiAlsaChannels, uiSamplerate, Fragments, FragmentSize)) { |
47 |
pcm_name = "hw:" + Card; |
pcm_name = "hw:" + Card; |
48 |
} |
} |
49 |
else { |
else { |
52 |
fflush(stdout); |
fflush(stdout); |
53 |
pcm_name = "plughw:" + Card; |
pcm_name = "plughw:" + Card; |
54 |
} |
} |
55 |
|
dmsg(1,("HW check completed.\n")); |
56 |
|
|
57 |
int err; |
int err; |
58 |
|
|
95 |
/* Set sample rate. If the exact rate is not supported */ |
/* Set sample rate. If the exact rate is not supported */ |
96 |
/* by the hardware, use nearest possible rate. */ |
/* by the hardware, use nearest possible rate. */ |
97 |
#if ALSA_MAJOR > 0 |
#if ALSA_MAJOR > 0 |
98 |
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &Samplerate, &dir)) < 0) |
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &uiSamplerate, &dir)) < 0) |
99 |
#else |
#else |
100 |
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, Samplerate, &dir)) < 0) |
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, uiSamplerate, &dir)) < 0) |
101 |
#endif |
#endif |
102 |
{ |
{ |
103 |
throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err)); |
throw AudioOutputException(String("Error setting sample rate: ") + snd_strerror(err)); |
104 |
} |
} |
105 |
|
|
106 |
if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, Channels)) < 0) { |
if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, uiAlsaChannels)) < 0) { |
107 |
throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err)); |
throw AudioOutputException(String("Error setting number of channels: ") + snd_strerror(err)); |
108 |
} |
} |
109 |
|
|
145 |
} |
} |
146 |
|
|
147 |
// allocate Alsa output buffer |
// allocate Alsa output buffer |
148 |
pAlsaOutputBuffer = new int16_t[Channels * FragmentSize]; |
pAlsaOutputBuffer = new int16_t[uiAlsaChannels * FragmentSize]; |
149 |
|
|
150 |
// create audio channels for this audio device to which the sampler engines can write to |
// create audio channels for this audio device to which the sampler engines can write to |
151 |
for (int i = 0; i < Channels; i++) this->Channels.push_back(new AudioChannel(FragmentSize)); |
for (int i = 0; i < uiAlsaChannels; i++) this->Channels.push_back(new AudioChannel(FragmentSize)); |
152 |
} |
} |
153 |
|
|
154 |
AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() { |
AudioOutputDeviceAlsa::~AudioOutputDeviceAlsa() { |
168 |
} |
} |
169 |
} |
} |
170 |
|
|
171 |
|
std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::CreateParameters(std::map<String,String> Parameters) { |
172 |
|
std::map<String,DeviceCreationParameter*> result; |
173 |
|
result["CARD"] = new ParameterCard(this, Parameters["CARD"]); // additional parameter, individually for this driver |
174 |
|
result["FRAGMENTS"] = new ParameterFragments(this, Parameters["FRAGMENTS"]); // additional parameter, individually for this driver |
175 |
|
result["FRAGMENTSIZE"] = new ParameterFragmentSize(this, Parameters["FRAGMENTSIZE"]); // additional parameter, individually for this driver |
176 |
|
return result; |
177 |
|
} |
178 |
|
|
179 |
/** |
/** |
180 |
* Checks if sound card supports the chosen parameters. |
* Checks if sound card supports the chosen parameters. |
181 |
* |
* |
182 |
* @returns true if hardware supports it |
* @returns true if hardware supports it |
183 |
*/ |
*/ |
184 |
bool AudioOutputDeviceAlsa::HardwareParametersSupported(uint channels, int samplerate, uint numfragments, uint fragmentsize) { |
bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) { |
185 |
pcm_name = "hw:0,0"; |
pcm_name = "hw:" + card; |
186 |
if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false; |
if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false; |
187 |
snd_pcm_hw_params_alloca(&hwparams); |
snd_pcm_hw_params_alloca(&hwparams); |
188 |
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { |
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { |
254 |
return uiSamplerate; |
return uiSamplerate; |
255 |
} |
} |
256 |
|
|
257 |
|
String AudioOutputDeviceAlsa::Description() { |
258 |
|
return "Advanced Linux Sound Architecture"; |
259 |
|
} |
260 |
|
|
261 |
|
String AudioOutputDeviceAlsa::Version() { |
262 |
|
String s = "$Revision: 1.4 $"; |
263 |
|
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
264 |
|
} |
265 |
|
|
266 |
|
std::map<String,DeviceCreationParameter*> AudioOutputDeviceAlsa::AvailableParameters() { |
267 |
|
// FIXME: not a good solution to get the commot parameters (ACTIVE,SAMPERATE,CHANNELS which have to be offered by all audio output drivers) |
268 |
|
std::map<String,DeviceCreationParameter*> available_parameters = AudioOutputDevice::AvailableParameters(); |
269 |
|
static ParameterCard param_card(NULL); |
270 |
|
static ParameterFragments param_fragments(NULL); |
271 |
|
static ParameterFragmentSize param_fragmentsize(NULL); |
272 |
|
available_parameters["CARD"] = ¶m_card; // additional parameter, individually for this driver |
273 |
|
available_parameters["FRAGMENTS"] = ¶m_fragments; // additional parameter, individually for this driver |
274 |
|
available_parameters["FRAGMENTSIZE"] = ¶m_fragmentsize; // additional parameter, individually for this driver |
275 |
|
return available_parameters; |
276 |
|
} |
277 |
|
|
278 |
|
/** |
279 |
|
* Entry point for the thread. |
280 |
|
*/ |
281 |
int AudioOutputDeviceAlsa::Main() { |
int AudioOutputDeviceAlsa::Main() { |
282 |
while (true) { |
while (true) { |
283 |
|
|