/[svn]/linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1764 - (show annotations) (download)
Sat Sep 6 14:55:18 2008 UTC (15 years, 7 months ago) by persson
File size: 14608 byte(s)
* rewrote the ConditionServer class to make it possible to create
  multiple sample channels when the audio thread isn't running

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "AudioOutputDeviceJack.h"
25 #include "AudioOutputDeviceFactory.h"
26
27 #include <errno.h>
28
29 #if HAVE_JACK
30
31 #ifndef HAVE_JACK_CLIENT_NAME_SIZE
32 #define jack_client_name_size() 33
33 #endif
34
35 namespace LinuxSampler {
36
37 /// number of currently existing JACK audio output devices in LinuxSampler
38 static int existingJackDevices = 0;
39
40 // *************** AudioChannelJack::ParameterName ***************
41 // *
42
43 AudioOutputDeviceJack::AudioChannelJack::ParameterName::ParameterName(AudioChannelJack* pChannel) : AudioChannel::ParameterName(ToString(pChannel->ChannelNr)) {
44 this->pChannel = pChannel;
45 }
46
47 void AudioOutputDeviceJack::AudioChannelJack::ParameterName::OnSetValue(String s) {
48 if (jack_port_set_name(pChannel->hJackPort, s.c_str())) throw AudioOutputException("Failed to rename JACK port");
49 }
50
51
52
53 // *************** AudioChannelJack::ParameterJackBindings ***************
54 // *
55
56 AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::ParameterJackBindings(AudioChannelJack* pChannel) : DeviceRuntimeParameterStrings(std::vector<String>()) {
57 this->pChannel = pChannel;
58 }
59
60 String AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::Description() {
61 return "Bindings to other JACK clients";
62 }
63
64 bool AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::Fix() {
65 return false;
66 }
67
68 std::vector<String> AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::PossibilitiesAsString() {
69 const char** pPortNames = jack_get_ports(pChannel->pDevice->hJackClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
70 if (!pPortNames) return std::vector<String>();
71 std::vector<String> result;
72 for (int i = 0; pPortNames[i]; i++) result.push_back(pPortNames[i]);
73 free(pPortNames);
74 return result;
75 }
76
77 void AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::OnSetValue(std::vector<String> vS) {
78 String src_name = ((DeviceCreationParameterString*)pChannel->pDevice->Parameters["NAME"])->ValueAsString() + ":" +
79 ((DeviceRuntimeParameterString*)pChannel->Parameters["NAME"])->ValueAsString();
80 // disconnect all current bindings first
81 for (int i = 0; i < Bindings.size(); i++) {
82 String dst_name = Bindings[i];
83 int res = jack_disconnect(pChannel->pDevice->hJackClient, src_name.c_str(), dst_name.c_str());
84 }
85 // connect new bindings
86 for (int i = 0; i < vS.size(); i++) {
87 String dst_name = vS[i];
88 int res = jack_connect(pChannel->pDevice->hJackClient, src_name.c_str(), dst_name.c_str());
89 if (res == EEXIST) throw AudioOutputException("Jack: Connection to port '" + dst_name + "' already established");
90 else if (res) throw AudioOutputException("Jack: Cannot connect port '" + src_name + "' to port '" + dst_name + "'");
91 }
92 // remember bindings
93 Bindings = vS;
94 }
95
96 String AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::Name() {
97 return "JACK_BINDINGS";
98 }
99
100
101
102 // *************** AudioChannelJack ***************
103 // *
104
105 AudioOutputDeviceJack::AudioChannelJack::AudioChannelJack(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) : AudioChannel(ChannelNr, CreateJackPort(ChannelNr, pDevice), pDevice->uiMaxSamplesPerCycle) {
106 this->pDevice = pDevice;
107 this->ChannelNr = ChannelNr;
108 delete Parameters["NAME"];
109 Parameters["NAME"] = new ParameterName(this);
110 Parameters["JACK_BINDINGS"] = new ParameterJackBindings(this);
111 }
112
113 AudioOutputDeviceJack::AudioChannelJack::~AudioChannelJack() {
114 //TODO: delete JACK port
115 }
116
117 float* AudioOutputDeviceJack::AudioChannelJack::CreateJackPort(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) {
118 String port_id = ToString(ChannelNr);
119 hJackPort = jack_port_register(pDevice->hJackClient, port_id.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
120 if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port.");
121 return (float*) jack_port_get_buffer(hJackPort, pDevice->uiMaxSamplesPerCycle);
122 }
123
124
125
126 // *************** AudioOutputDeviceJack::ParameterName ***************
127 // *
128
129 AudioOutputDeviceJack::ParameterName::ParameterName() : DeviceCreationParameterString() {
130 InitWithDefault(); // use default name
131 }
132
133 AudioOutputDeviceJack::ParameterName::ParameterName(String s) throw (Exception) : DeviceCreationParameterString(s) {
134 }
135
136 String AudioOutputDeviceJack::ParameterName::Description() {
137 return "Arbitrary JACK client name";
138 }
139
140 bool AudioOutputDeviceJack::ParameterName::Fix() {
141 return true;
142 }
143
144 bool AudioOutputDeviceJack::ParameterName::Mandatory() {
145 return false;
146 }
147
148 std::map<String,DeviceCreationParameter*> AudioOutputDeviceJack::ParameterName::DependsAsParameters() {
149 return std::map<String,DeviceCreationParameter*>(); // no dependencies
150 }
151
152 std::vector<String> AudioOutputDeviceJack::ParameterName::PossibilitiesAsString(std::map<String,String> Parameters) {
153 return std::vector<String>();
154 }
155
156 optional<String> AudioOutputDeviceJack::ParameterName::DefaultAsString(std::map<String,String> Parameters) {
157 return (existingJackDevices) ? "LinuxSampler" + ToString(existingJackDevices) : "LinuxSampler";
158 }
159
160 void AudioOutputDeviceJack::ParameterName::OnSetValue(String s) throw (Exception) {
161 // not possible, as parameter is fix
162 }
163
164 String AudioOutputDeviceJack::ParameterName::Name() {
165 return "NAME";
166 }
167
168
169
170 // *************** AudioOutputDeviceJack ***************
171 // *
172
173 /**
174 * Open and initialize connection to the JACK system.
175 *
176 * @param Parameters - optional parameters
177 * @throws AudioOutputException on error
178 * @see AcquireChannels()
179 */
180 AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {
181 JackClient* pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
182 existingJackDevices++;
183 pJackClient->SetAudioOutputDevice(this);
184 hJackClient = pJackClient->hJackClient;
185 uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);
186
187 // create audio channels
188 AcquireChannels(((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt());
189
190 // finally activate device if desired
191 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) Play();
192 }
193
194 AudioOutputDeviceJack::~AudioOutputDeviceJack() {
195 // destroy jack client if there is no midi device associated with it
196 JackClient::ReleaseAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
197 existingJackDevices--;
198 }
199
200 /**
201 * This method should not be called directly! It will be called by
202 * libjack to demand transmission of further sample points.
203 */
204 int AudioOutputDeviceJack::Process(uint Samples) {
205 int res;
206 if (csIsPlaying.Pop()) {
207 // let all connected engines render 'Samples' sample points
208 res = RenderAudio(Samples);
209 }
210 else {
211 // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio
212 res = RenderSilence(Samples);
213 }
214 csIsPlaying.RttDone();
215 return res;
216 }
217
218 void AudioOutputDeviceJack::Play() {
219 csIsPlaying.PushAndUnlock(true);
220 }
221
222 bool AudioOutputDeviceJack::IsPlaying() {
223 return csIsPlaying.GetUnsafe();
224 }
225
226 void AudioOutputDeviceJack::Stop() {
227 csIsPlaying.PushAndUnlock(false);
228 }
229
230 AudioChannel* AudioOutputDeviceJack::CreateChannel(uint ChannelNr) {
231 return new AudioChannelJack(ChannelNr, this);
232 }
233
234 uint AudioOutputDeviceJack::MaxSamplesPerCycle() {
235 return jack_get_buffer_size(hJackClient);
236 }
237
238 uint AudioOutputDeviceJack::SampleRate() {
239 return jack_get_sample_rate(hJackClient);
240 }
241
242 String AudioOutputDeviceJack::Name() {
243 return "JACK";
244 }
245
246 String AudioOutputDeviceJack::Driver() {
247 return Name();
248 }
249
250 String AudioOutputDeviceJack::Description() {
251 return "JACK Audio Connection Kit";
252 }
253
254 String AudioOutputDeviceJack::Version() {
255 String s = "$Revision: 1.25 $";
256 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
257 }
258
259
260
261 // *************** JackClient ***************
262 // *
263
264 // libjack callback functions
265
266 int linuxsampler_libjack_process_callback(jack_nframes_t nframes, void* arg) {
267 return static_cast<JackClient*>(arg)->Process(nframes);
268 }
269
270 void linuxsampler_libjack_shutdown_callback(void* arg) {
271 static_cast<JackClient*>(arg)->Stop();
272 fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");
273 }
274
275 std::map<String, JackClient*> JackClient::Clients;
276
277 int JackClient::Process(uint Samples) {
278 const config_t& config = ConfigReader.Lock();
279 #if HAVE_JACK_MIDI
280 if (config.MidiDevice) config.MidiDevice->Process(Samples);
281 #endif
282 int res = config.AudioDevice ? config.AudioDevice->Process(Samples) : 0;
283 ConfigReader.Unlock();
284 return res;
285 }
286
287 void JackClient::Stop() {
288 const config_t& config = ConfigReader.Lock();
289 if (config.AudioDevice) config.AudioDevice->Stop();
290 ConfigReader.Unlock();
291 }
292
293 JackClient::JackClient(String Name) : ConfigReader(Config) {
294 {
295 config_t& config = Config.GetConfigForUpdate();
296 config.AudioDevice = 0;
297 #if HAVE_JACK_MIDI
298 config.MidiDevice = 0;
299 #endif
300 }
301 {
302 config_t& config = Config.SwitchConfig();
303 config.AudioDevice = 0;
304 #if HAVE_JACK_MIDI
305 config.MidiDevice = 0;
306 #endif
307 }
308 audio = midi = false;
309 if (Name.size() >= jack_client_name_size())
310 throw Exception("JACK client name too long");
311 #if HAVE_JACK_CLIENT_OPEN
312 hJackClient = jack_client_open(Name.c_str(), JackNullOption, NULL);
313 #else
314 hJackClient = jack_client_new(Name.c_str());
315 #endif
316 if (!hJackClient)
317 throw Exception("Seems Jack server is not running.");
318 jack_set_process_callback(hJackClient, linuxsampler_libjack_process_callback, this);
319 jack_on_shutdown(hJackClient, linuxsampler_libjack_shutdown_callback, this);
320 if (jack_activate(hJackClient))
321 throw Exception("Jack: Cannot activate Jack client.");
322 }
323
324 JackClient::~JackClient() {
325 jack_client_close(hJackClient);
326 }
327
328 void JackClient::SetAudioOutputDevice(AudioOutputDeviceJack* device) {
329 Config.GetConfigForUpdate().AudioDevice = device;
330 Config.SwitchConfig().AudioDevice = device;
331 }
332
333 #if HAVE_JACK_MIDI
334 void JackClient::SetMidiInputDevice(MidiInputDeviceJack* device) {
335 Config.GetConfigForUpdate().MidiDevice = device;
336 Config.SwitchConfig().MidiDevice = device;
337 }
338 #endif
339
340 JackClient* JackClient::CreateAudio(String Name) { // static
341 JackClient* client;
342 std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
343 if (it == Clients.end()) {
344 client = new JackClient(Name);
345 Clients[Name] = client;
346 } else {
347 client = it->second;
348 if (client->audio) throw Exception("Jack audio device '" + Name + "' already exists");
349 }
350 client->audio = true;
351 return client;
352 }
353
354 JackClient* JackClient::CreateMidi(String Name) { // static
355 JackClient* client;
356 std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
357 if (it == Clients.end()) {
358 client = new JackClient(Name);
359 Clients[Name] = client;
360 } else {
361 client = it->second;
362 if (client->midi) throw Exception("Jack MIDI device '" + Name + "' already exists");
363 }
364 client->midi = true;
365 return client;
366 }
367
368 void JackClient::ReleaseAudio(String Name) { // static
369 JackClient* client = Clients[Name];
370 client->SetAudioOutputDevice(0);
371 client->audio = false;
372 if (!client->midi) {
373 Clients.erase(Name);
374 delete client;
375 }
376 }
377
378 void JackClient::ReleaseMidi(String Name) { // static
379 JackClient* client = Clients[Name];
380 #if HAVE_JACK_MIDI
381 client->SetMidiInputDevice(0);
382 #endif
383 client->midi = false;
384 if (!client->audio) {
385 Clients.erase(Name);
386 delete client;
387 }
388 }
389
390 } // namespace LinuxSampler
391
392 #endif // HAVE_JACK

  ViewVC Help
Powered by ViewVC