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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 221 by schoenebeck, Fri Aug 20 17:25:19 2004 UTC revision 2410 by schoenebeck, Sat Feb 2 18:52:15 2013 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6     *   Copyright (C) 2005 - 2013 Christian Schoenebeck                       *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 23  Line 24 
24  #include "AudioOutputDeviceJack.h"  #include "AudioOutputDeviceJack.h"
25  #include "AudioOutputDeviceFactory.h"  #include "AudioOutputDeviceFactory.h"
26    
27    #include <errno.h>
28    
29  #if HAVE_JACK  #if HAVE_JACK
30    
31    #ifndef HAVE_JACK_CLIENT_NAME_SIZE
32    #define jack_client_name_size() 33
33    #endif
34    
35  namespace LinuxSampler {  namespace LinuxSampler {
36    
37      REGISTER_AUDIO_OUTPUT_DRIVER(AudioOutputDeviceJack);      /// 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      /* Common parameters for now they'll have to be registered here. */  // *************** AudioChannelJack ***************
103      REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceJack, ParameterActive);  // *
104      REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceJack, ParameterSampleRate);  
105      REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceJack, ParameterChannels);      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        void AudioOutputDeviceJack::AudioChannelJack::UpdateJackBuffer(uint size) {
125            SetBuffer(
126               (float*)jack_port_get_buffer(hJackPort, size)
127            );
128        }
129    
130    
131    
132    // *************** AudioOutputDeviceJack::ParameterName ***************
133    // *
134    
135        AudioOutputDeviceJack::ParameterName::ParameterName() : DeviceCreationParameterString() {
136            InitWithDefault(); // use default name
137        }
138    
139        AudioOutputDeviceJack::ParameterName::ParameterName(String s) throw (Exception) : DeviceCreationParameterString(s) {
140        }
141    
142        String AudioOutputDeviceJack::ParameterName::Description() {
143            return "Arbitrary JACK client name";
144        }
145    
146        bool AudioOutputDeviceJack::ParameterName::Fix() {
147            return true;
148        }
149    
150        bool AudioOutputDeviceJack::ParameterName::Mandatory() {
151            return false;
152        }
153    
154        std::map<String,DeviceCreationParameter*> AudioOutputDeviceJack::ParameterName::DependsAsParameters() {
155            return std::map<String,DeviceCreationParameter*>(); // no dependencies
156        }
157    
158        std::vector<String> AudioOutputDeviceJack::ParameterName::PossibilitiesAsString(std::map<String,String> Parameters) {
159            return std::vector<String>();
160        }
161    
162        optional<String> AudioOutputDeviceJack::ParameterName::DefaultAsString(std::map<String,String> Parameters) {
163            return (existingJackDevices) ? "LinuxSampler" + ToString(existingJackDevices) : "LinuxSampler";
164        }
165    
166        void AudioOutputDeviceJack::ParameterName::OnSetValue(String s) throw (Exception) {
167            // not possible, as parameter is fix
168        }
169    
170        String AudioOutputDeviceJack::ParameterName::Name() {
171            return "NAME";
172        }
173    
174    
175    
176    // *************** AudioOutputDeviceJack ***************
177    // *
178    
179      /**      /**
180       * Open and initialize connection to the JACK system.       * Open and initialize connection to the JACK system.
# Line 41  namespace LinuxSampler { Line 183  namespace LinuxSampler {
183       * @throws AudioOutputException  on error       * @throws AudioOutputException  on error
184       * @see AcquireChannels()       * @see AcquireChannels()
185       */       */
186      AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(std::map<String,DeviceCreationParameter*>()) {      AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {
187          if ((hJackClient = jack_client_new("LinuxSampler")) == 0)          JackClient* pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
188              throw AudioOutputException("Seems Jack server not running.");          existingJackDevices++;
189            pJackClient->SetAudioOutputDevice(this);
190          jack_set_process_callback(hJackClient, __libjack_process_callback, this);          hJackClient = pJackClient->hJackClient;
         jack_on_shutdown(hJackClient, __libjack_shutdown_callback, this);  
         if (jack_activate(hJackClient))  
             throw AudioOutputException("Jack: Cannot activate Jack client.");  
   
191          uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);          uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);
192    
193  #if 0          // create audio channels
194          // create amount of audio channels and jack output ports we need for autoconnect          AcquireChannels(((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt());
195          for (uint p = 0; p < AutoConnectPorts; p++) {  
196              // create jack output port          // finally activate device if desired
197              std::stringstream portid; portid << "LinuxSampler:" << p;          if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) Play();
             jack_port_t* newport;  
             if (newport = jack_port_register(hJackClient, portid.str().c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) {  
                 hJackPorts.push_back(newport);  
             }  
             else throw AudioOutputException("Jack: Cannot register Jack output port.");  
   
             // create LS audio channel  
             std::stringstream chanid; chanid << "Jack:" << p;  
             Channels.push_back(new AudioChannel((float*) jack_port_get_buffer(newport, uiMaxSamplesPerCycle), uiMaxSamplesPerCycle, chanid.str()));  
   
             // autoconnect port  
             if (jack_connect(hJackClient, portid.str().c_str(), AutoConnectPortIDs[p].c_str())) {  
                 std::stringstream err; err << "Jack: Cannot auto connect port " << p;  
                 throw AudioOutputException(err.str());  
             }  
         }  
 #endif  
198      }      }
199    
200      AudioOutputDeviceJack::~AudioOutputDeviceJack() {      AudioOutputDeviceJack::~AudioOutputDeviceJack() {
201          // destroy all audio channels          // destroy jack client if there is no midi device associated with it
202          for (int c = 0; c < Channels.size(); c++) delete Channels[c];          JackClient::ReleaseAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
203          // destroy jack client          existingJackDevices--;
         jack_client_close(hJackClient);  
204      }      }
205    
206      /**      /**
# Line 88  namespace LinuxSampler { Line 208  namespace LinuxSampler {
208       * libjack to demand transmission of further sample points.       * libjack to demand transmission of further sample points.
209       */       */
210      int AudioOutputDeviceJack::Process(uint Samples) {      int AudioOutputDeviceJack::Process(uint Samples) {
211            int res;
212            
213            // in recent versions of JACK2 and JACk1, we are forced to
214            // re-retrieve the audio buffer pointers in each process period
215            UpdateJackBuffers(Samples);
216            
217          if (csIsPlaying.Pop()) {          if (csIsPlaying.Pop()) {
218              // let all connected engines render 'Samples' sample points              // let all connected engines render 'Samples' sample points
219              return RenderAudio(Samples);              res = RenderAudio(Samples);
220          }          }
221          else {          else {
222              // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio              // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio
223              return RenderSilence(Samples);              res = RenderSilence(Samples);
224          }          }
225            csIsPlaying.RttDone();
226            return res;
227        }
228        
229        void AudioOutputDeviceJack::UpdateJackBuffers(uint size) {
230            for (int i = 0; i < Channels.size(); ++i)
231                static_cast<AudioChannelJack*>(Channels[i])->UpdateJackBuffer(size);
232        }
233        
234        float AudioOutputDeviceJack::latency() {
235            if (!hJackClient) return -1;
236            const float size = jack_get_buffer_size(hJackClient);
237            const float rate = jack_get_sample_rate(hJackClient);
238            return size / rate;
239      }      }
240    
241      void AudioOutputDeviceJack::Play() {      void AudioOutputDeviceJack::Play() {
# Line 103  namespace LinuxSampler { Line 243  namespace LinuxSampler {
243      }      }
244    
245      bool AudioOutputDeviceJack::IsPlaying() {      bool AudioOutputDeviceJack::IsPlaying() {
246          csIsPlaying.GetUnsafe();          return csIsPlaying.GetUnsafe();
247      }      }
248    
249      void AudioOutputDeviceJack::Stop() {      void AudioOutputDeviceJack::Stop() {
250          csIsPlaying.PushAndUnlock(false);          csIsPlaying.PushAndUnlock(false);
251      }      }
252    
253      void AudioOutputDeviceJack::AcquireChannels(uint uiChannels) {      AudioChannel* AudioOutputDeviceJack::CreateChannel(uint ChannelNr) {
254          if (uiChannels > this->Channels.size()) {          return new AudioChannelJack(ChannelNr, this);
             for (int c = this->Channels.size(); c < uiChannels; c++) {  
                 // create new jack output port  
                 std::stringstream portid; portid << "LinuxSampler:" << c;  
                 jack_port_t* newport;  
                 if (newport = jack_port_register(hJackClient, portid.str().c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) {  
                     hJackPorts.push_back(newport);  
                 }  
                 else throw AudioOutputException("Jack: Cannot register Jack output port.");  
   
                 // create LS audio channel  
                 std::stringstream chanid; chanid << "Jack:" << c;  
                 Channels.push_back(new AudioChannel((float*) jack_port_get_buffer(newport, uiMaxSamplesPerCycle), uiMaxSamplesPerCycle, chanid.str()));  
             }  
         }  
255      }      }
256    
257      uint AudioOutputDeviceJack::MaxSamplesPerCycle() {      uint AudioOutputDeviceJack::MaxSamplesPerCycle() {
# Line 149  namespace LinuxSampler { Line 275  namespace LinuxSampler {
275      }      }
276    
277      String AudioOutputDeviceJack::Version() {      String AudioOutputDeviceJack::Version() {
278         String s = "$Revision: 1.10 $";         String s = "$Revision: 1.25 $";
279         return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword         return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
280      }      }
281    
282    
283    
284    // *************** JackClient ***************
285    // *
286    
287      // libjack callback functions      // libjack callback functions
288    
289      int __libjack_process_callback(jack_nframes_t nframes, void* arg) {      int linuxsampler_libjack_process_callback(jack_nframes_t nframes, void* arg) {
290          AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;          return static_cast<JackClient*>(arg)->Process(nframes);
         return pAudioOutputDeviceJack->Process(nframes);  
291      }      }
292    
293      void __libjack_shutdown_callback(void* arg) {      void linuxsampler_libjack_shutdown_callback(void* arg) {
294          AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;          static_cast<JackClient*>(arg)->Stop();
         pAudioOutputDeviceJack->Stop();  
295          fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");          fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");
296      }      }
297        
298        int JackClient::libjackSampleRateCallback(jack_nframes_t nframes, void *arg) {
299            JackClient* client = static_cast<JackClient*>(arg);
300            const config_t& config = client->ConfigReader.Lock();
301            if (config.AudioDevice)
302                config.AudioDevice->ReconnectAll();
303            client->ConfigReader.Unlock();
304            return 0;
305        }
306        
307        int JackClient::libjackBufferSizeCallback(jack_nframes_t nframes, void *arg) {
308            dmsg(1,("libjackBufferSizeCallback(%d)\n",nframes));
309            JackClient* client = static_cast<JackClient*>(arg);
310            const config_t& config = client->ConfigReader.Lock();
311            if (config.AudioDevice) {
312                config.AudioDevice->UpdateJackBuffers(nframes);
313                config.AudioDevice->ReconnectAll();
314            }
315            client->ConfigReader.Unlock();
316            return 0;
317        }
318    
319        std::map<String, JackClient*> JackClient::Clients;
320    
321        int JackClient::Process(uint Samples) {
322            const config_t& config = ConfigReader.Lock();
323    #if HAVE_JACK_MIDI
324            if (config.MidiDevice) config.MidiDevice->Process(Samples);
325    #endif
326            int res = config.AudioDevice ? config.AudioDevice->Process(Samples) : 0;
327            ConfigReader.Unlock();
328            return res;
329        }
330    
331        void JackClient::Stop() {
332            const config_t& config = ConfigReader.Lock();
333            if (config.AudioDevice) config.AudioDevice->Stop();
334            ConfigReader.Unlock();
335        }
336    
337        JackClient::JackClient(String Name) : ConfigReader(Config) {
338            {
339                config_t& config = Config.GetConfigForUpdate();
340                config.AudioDevice = 0;
341    #if HAVE_JACK_MIDI
342                config.MidiDevice = 0;
343    #endif
344            }
345            {
346                config_t& config = Config.SwitchConfig();
347                config.AudioDevice = 0;
348    #if HAVE_JACK_MIDI
349                config.MidiDevice = 0;
350    #endif
351            }
352            audio = midi = false;
353            if (Name.size() >= jack_client_name_size())
354                throw Exception("JACK client name too long");
355    #if HAVE_JACK_CLIENT_OPEN
356            hJackClient = jack_client_open(Name.c_str(), JackNullOption, NULL);
357    #else
358            hJackClient = jack_client_new(Name.c_str());
359    #endif
360            if (!hJackClient)
361                throw Exception("Seems Jack server is not running.");
362            jack_set_process_callback(hJackClient, linuxsampler_libjack_process_callback, this);
363            jack_on_shutdown(hJackClient, linuxsampler_libjack_shutdown_callback, this);
364            jack_set_buffer_size_callback(hJackClient, libjackBufferSizeCallback, this);
365            jack_set_sample_rate_callback(hJackClient, libjackSampleRateCallback, this);
366            
367            if (jack_activate(hJackClient))
368                throw Exception("Jack: Cannot activate Jack client.");
369        }
370    
371        JackClient::~JackClient() {
372            jack_client_close(hJackClient);
373        }
374    
375        void JackClient::SetAudioOutputDevice(AudioOutputDeviceJack* device) {
376            Config.GetConfigForUpdate().AudioDevice = device;
377            Config.SwitchConfig().AudioDevice = device;
378        }
379    
380    #if HAVE_JACK_MIDI
381        void JackClient::SetMidiInputDevice(MidiInputDeviceJack* device) {
382            Config.GetConfigForUpdate().MidiDevice = device;
383            Config.SwitchConfig().MidiDevice = device;
384        }
385    #endif
386    
387        JackClient* JackClient::CreateAudio(String Name) { // static
388            JackClient* client;
389            std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
390            if (it == Clients.end()) {
391                client = new JackClient(Name);
392                Clients[Name] = client;
393            } else {
394                client = it->second;
395                if (client->audio) throw Exception("Jack audio device '" + Name + "' already exists");
396            }
397            client->audio = true;
398            return client;
399        }
400    
401        JackClient* JackClient::CreateMidi(String Name) { // static
402            JackClient* client;
403            std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
404            if (it == Clients.end()) {
405                client = new JackClient(Name);
406                Clients[Name] = client;
407            } else {
408                client = it->second;
409                if (client->midi) throw Exception("Jack MIDI device '" + Name + "' already exists");
410            }
411            client->midi = true;
412            return client;
413        }
414    
415        void JackClient::ReleaseAudio(String Name) { // static
416            JackClient* client = Clients[Name];
417            client->SetAudioOutputDevice(0);
418            client->audio = false;
419            if (!client->midi) {
420                Clients.erase(Name);
421                delete client;
422            }
423        }
424    
425        void JackClient::ReleaseMidi(String Name) { // static
426            JackClient* client = Clients[Name];
427    #if HAVE_JACK_MIDI
428            client->SetMidiInputDevice(0);
429    #endif
430            client->midi = false;
431            if (!client->audio) {
432                Clients.erase(Name);
433                delete client;
434            }
435        }
436    
437  } // namespace LinuxSampler  } // namespace LinuxSampler
438    

Legend:
Removed from v.221  
changed lines
  Added in v.2410

  ViewVC Help
Powered by ViewVC