/[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 483 - (show annotations) (download)
Mon Mar 21 23:40:56 2005 UTC (19 years ago) by schoenebeck
File size: 11210 byte(s)
* JACK audio driver: fixed handling of channel parameter 'JACK_BINDINGS'
* fixed parser bug of multiplicity parameters

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 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, NULL, 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); FIXME: pPortNames should be freed here
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 Parameters["NAME"] = new ParameterName(this);
109 Parameters["JACK_BINDINGS"] = new ParameterJackBindings(this);
110 }
111
112 AudioOutputDeviceJack::AudioChannelJack::~AudioChannelJack() {
113 //TODO: delete JACK port
114 }
115
116 float* AudioOutputDeviceJack::AudioChannelJack::CreateJackPort(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) {
117 String port_id = ToString(ChannelNr);
118 hJackPort = jack_port_register(pDevice->hJackClient, port_id.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
119 if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port.");
120 return (float*) jack_port_get_buffer(hJackPort, pDevice->uiMaxSamplesPerCycle);
121 }
122
123
124
125 // *************** AudioOutputDeviceJack::ParameterName ***************
126 // *
127
128 AudioOutputDeviceJack::ParameterName::ParameterName() : DeviceCreationParameterString() {
129 InitWithDefault(); // use default name
130 }
131
132 AudioOutputDeviceJack::ParameterName::ParameterName(String s) throw (LinuxSamplerException) : DeviceCreationParameterString(s) {
133 }
134
135 String AudioOutputDeviceJack::ParameterName::Description() {
136 return "Arbitrary JACK client name";
137 }
138
139 bool AudioOutputDeviceJack::ParameterName::Fix() {
140 return true;
141 }
142
143 bool AudioOutputDeviceJack::ParameterName::Mandatory() {
144 return false;
145 }
146
147 std::map<String,DeviceCreationParameter*> AudioOutputDeviceJack::ParameterName::DependsAsParameters() {
148 return std::map<String,DeviceCreationParameter*>(); // no dependencies
149 }
150
151 std::vector<String> AudioOutputDeviceJack::ParameterName::PossibilitiesAsString(std::map<String,String> Parameters) {
152 return std::vector<String>();
153 }
154
155 optional<String> AudioOutputDeviceJack::ParameterName::DefaultAsString(std::map<String,String> Parameters) {
156 return (existingJackDevices) ? "LinuxSampler" + ToString(existingJackDevices) : "LinuxSampler";
157 }
158
159 void AudioOutputDeviceJack::ParameterName::OnSetValue(String s) throw (LinuxSamplerException) {
160 // not possible, as parameter is fix
161 }
162
163 String AudioOutputDeviceJack::ParameterName::Name() {
164 return "NAME";
165 }
166
167
168
169 // *************** AudioOutputDeviceJack ***************
170 // *
171
172 /**
173 * Open and initialize connection to the JACK system.
174 *
175 * @param Parameters - optional parameters
176 * @throws AudioOutputException on error
177 * @see AcquireChannels()
178 */
179 AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {
180 if (((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString().size() >= jack_client_name_size())
181 throw LinuxSamplerException("JACK client name too long");
182
183 if ((hJackClient = jack_client_new(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString().c_str())) == 0)
184 throw AudioOutputException("Seems Jack server not running.");
185
186 existingJackDevices++;
187
188 jack_set_process_callback(hJackClient, __libjack_process_callback, this);
189 jack_on_shutdown(hJackClient, __libjack_shutdown_callback, this);
190 if (jack_activate(hJackClient))
191 throw AudioOutputException("Jack: Cannot activate Jack client.");
192
193 uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);
194
195 // create audio channels
196 AcquireChannels(((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt());
197
198 // finally activate device if desired
199 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) Play();
200 }
201
202 AudioOutputDeviceJack::~AudioOutputDeviceJack() {
203 // destroy jack client
204 jack_client_close(hJackClient);
205 existingJackDevices--;
206 }
207
208 /**
209 * This method should not be called directly! It will be called by
210 * libjack to demand transmission of further sample points.
211 */
212 int AudioOutputDeviceJack::Process(uint Samples) {
213 if (csIsPlaying.Pop()) {
214 // let all connected engines render 'Samples' sample points
215 return RenderAudio(Samples);
216 }
217 else {
218 // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio
219 return RenderSilence(Samples);
220 }
221 }
222
223 void AudioOutputDeviceJack::Play() {
224 csIsPlaying.PushAndUnlock(true);
225 }
226
227 bool AudioOutputDeviceJack::IsPlaying() {
228 csIsPlaying.GetUnsafe();
229 }
230
231 void AudioOutputDeviceJack::Stop() {
232 csIsPlaying.PushAndUnlock(false);
233 }
234
235 AudioChannel* AudioOutputDeviceJack::CreateChannel(uint ChannelNr) {
236 return new AudioChannelJack(ChannelNr, this);
237 }
238
239 uint AudioOutputDeviceJack::MaxSamplesPerCycle() {
240 return jack_get_buffer_size(hJackClient);
241 }
242
243 uint AudioOutputDeviceJack::SampleRate() {
244 return jack_get_sample_rate(hJackClient);
245 }
246
247 String AudioOutputDeviceJack::Name() {
248 return "JACK";
249 }
250
251 String AudioOutputDeviceJack::Driver() {
252 return Name();
253 }
254
255 String AudioOutputDeviceJack::Description() {
256 return "JACK Audio Connection Kit";
257 }
258
259 String AudioOutputDeviceJack::Version() {
260 String s = "$Revision: 1.18 $";
261 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
262 }
263
264 // libjack callback functions
265
266 int __libjack_process_callback(jack_nframes_t nframes, void* arg) {
267 AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;
268 return pAudioOutputDeviceJack->Process(nframes);
269 }
270
271 void __libjack_shutdown_callback(void* arg) {
272 AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;
273 pAudioOutputDeviceJack->Stop();
274 fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");
275 }
276
277 } // namespace LinuxSampler
278
279 #endif // HAVE_JACK

  ViewVC Help
Powered by ViewVC