/[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 268 by capela, Thu Oct 7 22:20:20 2004 UTC revision 2512 by schoenebeck, Sun Jan 26 17:47:43 2014 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 - 2014 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 27  Line 28 
28    
29  #if HAVE_JACK  #if HAVE_JACK
30    
31  namespace LinuxSampler {  #ifndef HAVE_JACK_CLIENT_NAME_SIZE
32    #define jack_client_name_size() 33
33      REGISTER_AUDIO_OUTPUT_DRIVER(AudioOutputDeviceJack);  #endif
   
     /* Common parameters for now they'll have to be registered here. */  
     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceJack, ParameterActive);  
     REGISTER_AUDIO_OUTPUT_DRIVER_PARAMETER(AudioOutputDeviceJack, ParameterChannels);  
34    
35    namespace LinuxSampler {
36    
37        /// number of currently existing JACK audio output devices in LinuxSampler
38        static int existingJackDevices = 0;
39    
40  // *************** ParameterName ***************  // *************** AudioChannelJack::ParameterName ***************
41  // *  // *
42    
43      AudioOutputDeviceJack::AudioChannelJack::ParameterName::ParameterName(AudioChannelJack* pChannel) : AudioChannel::ParameterName(ToString(pChannel->ChannelNr)) {      AudioOutputDeviceJack::AudioChannelJack::ParameterName::ParameterName(AudioChannelJack* pChannel) : AudioChannel::ParameterName(ToString(pChannel->ChannelNr)) {
# Line 50  namespace LinuxSampler { Line 50  namespace LinuxSampler {
50    
51    
52    
53  // *************** ParameterJackBindings ***************  // *************** AudioChannelJack::ParameterJackBindings ***************
54  // *  // *
55    
56      AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::ParameterJackBindings(AudioChannelJack* pChannel) : DeviceRuntimeParameterStrings(std::vector<String>()) {      AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::ParameterJackBindings(AudioChannelJack* pChannel) : DeviceRuntimeParameterStrings(std::vector<String>()) {
# Line 66  namespace LinuxSampler { Line 66  namespace LinuxSampler {
66      }      }
67    
68      std::vector<String> AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::PossibilitiesAsString() {      std::vector<String> AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::PossibilitiesAsString() {
69          const char** pPortNames = jack_get_ports(pChannel->pDevice->hJackClient, NULL, NULL, JackPortIsInput);          const char** pPortNames = jack_get_ports(pChannel->pDevice->hJackClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
70          if (!pPortNames) return std::vector<String>();          if (!pPortNames) return std::vector<String>();
71          std::vector<String> result;          std::vector<String> result;
72          for (int i = 0; pPortNames[i]; i++) result.push_back(pPortNames[i]);          for (int i = 0; pPortNames[i]; i++) result.push_back(pPortNames[i]);
73          //free(pPortNames); FIXME: pPortNames should be freed here          free(pPortNames);
74          return result;          return result;
75      }      }
76    
77      void AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::OnSetValue(std::vector<String> vS) {      void AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::OnSetValue(std::vector<String> vS) {
78          // TODO: we should remove all existing bindings before we connect new ones here          String src_name = ((DeviceCreationParameterString*)pChannel->pDevice->Parameters["NAME"])->ValueAsString() + ":" +
79          String src_name = "LinuxSampler:" + ((DeviceRuntimeParameterString*)pChannel->Parameters["NAME"])->ValueAsString();                            ((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++) {          for (int i = 0; i < vS.size(); i++) {
87              String dst_name = vS[i];              String dst_name = vS[i];
88              int res = jack_connect(pChannel->pDevice->hJackClient, src_name.c_str(), dst_name.c_str());              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");              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 + "'");              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() {      String AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::Name() {
# Line 97  namespace LinuxSampler { Line 105  namespace LinuxSampler {
105      AudioOutputDeviceJack::AudioChannelJack::AudioChannelJack(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) : AudioChannel(ChannelNr, CreateJackPort(ChannelNr, pDevice), pDevice->uiMaxSamplesPerCycle) {      AudioOutputDeviceJack::AudioChannelJack::AudioChannelJack(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) : AudioChannel(ChannelNr, CreateJackPort(ChannelNr, pDevice), pDevice->uiMaxSamplesPerCycle) {
106          this->pDevice   = pDevice;          this->pDevice   = pDevice;
107          this->ChannelNr = ChannelNr;          this->ChannelNr = ChannelNr;
108            delete Parameters["NAME"];
109          Parameters["NAME"]          = new ParameterName(this);          Parameters["NAME"]          = new ParameterName(this);
110          Parameters["JACK_BINDINGS"] = new ParameterJackBindings(this);          Parameters["JACK_BINDINGS"] = new ParameterJackBindings(this);
111      }      }
# Line 111  namespace LinuxSampler { Line 120  namespace LinuxSampler {
120          if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port.");          if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port.");
121          return (float*) jack_port_get_buffer(hJackPort, pDevice->uiMaxSamplesPerCycle);          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::ParameterSampleRate ***************
177    // *
178    
179        AudioOutputDeviceJack::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate() {
180        }
181    
182        AudioOutputDeviceJack::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate(s) {
183        }
184    
185        optional<int> AudioOutputDeviceJack::ParameterSampleRate::DefaultAsInt(std::map<String,String> Parameters) {
186            static int i = 0;
187            // arbitrary JACK client name, that shall not clash with other ones
188            String name = "LinSmPSR" + ToString(i++);
189    #if HAVE_JACK_CLIENT_OPEN
190            jack_client_t* hClient = jack_client_open(name.c_str(), JackNullOption, NULL);
191    #else
192            jack_client_t* hClient = jack_client_new(name.c_str());
193    #endif
194            // Better return "nothing" instead of i.e. 44100 here, so a frontend
195            // like QSampler will not be tempted to pass SAMPLERATE as creation
196            // parameter when creating a new JACK audio device instance.
197            if (!hClient) return optional<int>::nothing;
198            int sampleRate = jack_get_sample_rate(hClient);
199            jack_client_close(hClient);
200            return sampleRate;
201        }
202    
203        void AudioOutputDeviceJack::ParameterSampleRate::OnSetValue(int i) throw (Exception) {
204            // nothing to do: the JACK API does currently not allow JACK clients to
205            // change the sample rate
206        }
207    
208    
209    
# Line 125  namespace LinuxSampler { Line 218  namespace LinuxSampler {
218       * @see AcquireChannels()       * @see AcquireChannels()
219       */       */
220      AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {      AudioOutputDeviceJack::AudioOutputDeviceJack(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {
221          if ((hJackClient = jack_client_new("LinuxSampler")) == 0)          pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
222              throw AudioOutputException("Seems Jack server not running.");          existingJackDevices++;
223            pJackClient->SetAudioOutputDevice(this);
224          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.");  
   
225          uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);          uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient);
226    
227          // create audio channels          // create audio channels
# Line 143  namespace LinuxSampler { Line 232  namespace LinuxSampler {
232      }      }
233    
234      AudioOutputDeviceJack::~AudioOutputDeviceJack() {      AudioOutputDeviceJack::~AudioOutputDeviceJack() {
235          // destroy jack client          // destroy jack client if there is no midi device associated with it
236          jack_client_close(hJackClient);          JackClient::ReleaseAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString());
237            existingJackDevices--;
238      }      }
239    
240      /**      /**
# Line 152  namespace LinuxSampler { Line 242  namespace LinuxSampler {
242       * libjack to demand transmission of further sample points.       * libjack to demand transmission of further sample points.
243       */       */
244      int AudioOutputDeviceJack::Process(uint Samples) {      int AudioOutputDeviceJack::Process(uint Samples) {
245            int res;
246            
247            // in recent versions of JACK2 and JACk1, we are forced to
248            // re-retrieve the audio buffer pointers in each process period
249            UpdateJackBuffers(Samples);
250            
251          if (csIsPlaying.Pop()) {          if (csIsPlaying.Pop()) {
252              // let all connected engines render 'Samples' sample points              // let all connected engines render 'Samples' sample points
253              return RenderAudio(Samples);              res = RenderAudio(Samples);
254          }          }
255          else {          else {
256              // 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
257              return RenderSilence(Samples);              res = RenderSilence(Samples);
258          }          }
259            csIsPlaying.RttDone();
260            return res;
261        }
262        
263        void AudioOutputDeviceJack::UpdateJackBuffers(uint size) {
264            for (int i = 0; i < Channels.size(); ++i)
265                static_cast<AudioChannelJack*>(Channels[i])->UpdateJackBuffer(size);
266        }
267        
268        float AudioOutputDeviceJack::latency() {
269            if (!hJackClient) return -1;
270            const float size = jack_get_buffer_size(hJackClient);
271            const float rate = jack_get_sample_rate(hJackClient);
272            return size / rate;
273        }
274        
275        jack_client_t* AudioOutputDeviceJack::jackClientHandle() {
276            return hJackClient;
277      }      }
278    
279      void AudioOutputDeviceJack::Play() {      void AudioOutputDeviceJack::Play() {
# Line 167  namespace LinuxSampler { Line 281  namespace LinuxSampler {
281      }      }
282    
283      bool AudioOutputDeviceJack::IsPlaying() {      bool AudioOutputDeviceJack::IsPlaying() {
284          csIsPlaying.GetUnsafe();          return csIsPlaying.GetUnsafe();
285      }      }
286    
287      void AudioOutputDeviceJack::Stop() {      void AudioOutputDeviceJack::Stop() {
# Line 185  namespace LinuxSampler { Line 299  namespace LinuxSampler {
299      uint AudioOutputDeviceJack::SampleRate() {      uint AudioOutputDeviceJack::SampleRate() {
300          return jack_get_sample_rate(hJackClient);          return jack_get_sample_rate(hJackClient);
301      }      }
302        
303        void AudioOutputDeviceJack::addListener(JackListener* listener) {
304            pJackClient->addListener(listener);
305        }
306    
307      String AudioOutputDeviceJack::Name() {      String AudioOutputDeviceJack::Name() {
308          return "JACK";          return "JACK";
# Line 199  namespace LinuxSampler { Line 317  namespace LinuxSampler {
317      }      }
318    
319      String AudioOutputDeviceJack::Version() {      String AudioOutputDeviceJack::Version() {
320         String s = "$Revision: 1.13 $";         String s = "$Revision$";
321         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
322      }      }
323    
324    
325    
326    // *************** JackClient ***************
327    // *
328    
329      // libjack callback functions      // libjack callback functions
330    
331      int __libjack_process_callback(jack_nframes_t nframes, void* arg) {      int linuxsampler_libjack_process_callback(jack_nframes_t nframes, void* arg) {
332          AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;          return static_cast<JackClient*>(arg)->Process(nframes);
         return pAudioOutputDeviceJack->Process(nframes);  
333      }      }
334    
335      void __libjack_shutdown_callback(void* arg) {  #if HAVE_JACK_ON_INFO_SHUTDOWN
336          AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg;      void JackClient::libjackShutdownCallback(jack_status_t code, const char* reason, void *arg)
337          pAudioOutputDeviceJack->Stop();  #else
338        void JackClient::libjackShutdownCallback(void *arg)
339    #endif
340        {
341            JackClient* jackClient = static_cast<JackClient*>(arg);
342            jackClient->Stop();
343          fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");          fprintf(stderr, "Jack: Jack server shutdown, exiting.\n");
344            for (int i = 0; i < jackClient->jackListeners.size(); ++i) {
345                JackListener* listener = jackClient->jackListeners[i];
346    #if HAVE_JACK_ON_INFO_SHUTDOWN
347                listener->onJackShutdown(code, reason);
348    #else
349                listener->onJackShutdown(JackFailure, "unknown");
350    #endif
351            }
352        }
353        
354        int JackClient::libjackSampleRateCallback(jack_nframes_t nframes, void *arg) {
355            JackClient* client = static_cast<JackClient*>(arg);
356            const config_t& config = client->ConfigReader.Lock();
357            if (config.AudioDevice)
358                config.AudioDevice->ReconnectAll();
359            client->ConfigReader.Unlock();
360            return 0;
361        }
362        
363        int JackClient::libjackBufferSizeCallback(jack_nframes_t nframes, void *arg) {
364            dmsg(1,("libjackBufferSizeCallback(%d)\n",nframes));
365            JackClient* client = static_cast<JackClient*>(arg);
366            const config_t& config = client->ConfigReader.Lock();
367            if (config.AudioDevice) {
368                config.AudioDevice->UpdateJackBuffers(nframes);
369                config.AudioDevice->ReconnectAll();
370            }
371            client->ConfigReader.Unlock();
372            return 0;
373        }
374        
375        void JackClient::addListener(JackListener* listener) {
376            jackListeners.push_back(listener);
377        }
378    
379        std::map<String, JackClient*> JackClient::Clients;
380    
381        int JackClient::Process(uint Samples) {
382            const config_t& config = ConfigReader.Lock();
383    #if HAVE_JACK_MIDI
384            if (config.MidiDevice) config.MidiDevice->Process(Samples);
385    #endif
386            int res = config.AudioDevice ? config.AudioDevice->Process(Samples) : 0;
387            ConfigReader.Unlock();
388            return res;
389        }
390    
391        void JackClient::Stop() {
392            const config_t& config = ConfigReader.Lock();
393            if (config.AudioDevice) config.AudioDevice->Stop();
394            ConfigReader.Unlock();
395        }
396    
397        JackClient::JackClient(String Name) : ConfigReader(Config) {
398            {
399                config_t& config = Config.GetConfigForUpdate();
400                config.AudioDevice = 0;
401    #if HAVE_JACK_MIDI
402                config.MidiDevice = 0;
403    #endif
404            }
405            {
406                config_t& config = Config.SwitchConfig();
407                config.AudioDevice = 0;
408    #if HAVE_JACK_MIDI
409                config.MidiDevice = 0;
410    #endif
411            }
412            audio = midi = false;
413            if (Name.size() >= jack_client_name_size())
414                throw Exception("JACK client name too long");
415    #if HAVE_JACK_CLIENT_OPEN
416            hJackClient = jack_client_open(Name.c_str(), JackNullOption, NULL);
417    #else
418            hJackClient = jack_client_new(Name.c_str());
419    #endif
420            if (!hJackClient)
421                throw Exception("Seems Jack server is not running.");
422            jack_set_process_callback(hJackClient, linuxsampler_libjack_process_callback, this);
423    #if HAVE_JACK_ON_INFO_SHUTDOWN
424            jack_on_info_shutdown(hJackClient, libjackShutdownCallback, this);
425    #else
426            jack_on_shutdown(hJackClient, libjackShutdownCallback, this);
427    #endif
428            jack_set_buffer_size_callback(hJackClient, libjackBufferSizeCallback, this);
429            jack_set_sample_rate_callback(hJackClient, libjackSampleRateCallback, this);
430            
431            if (jack_activate(hJackClient))
432                throw Exception("Jack: Cannot activate Jack client.");
433        }
434    
435        JackClient::~JackClient() {
436            jack_client_close(hJackClient);
437        }
438    
439        void JackClient::SetAudioOutputDevice(AudioOutputDeviceJack* device) {
440            Config.GetConfigForUpdate().AudioDevice = device;
441            Config.SwitchConfig().AudioDevice = device;
442        }
443    
444    #if HAVE_JACK_MIDI
445        void JackClient::SetMidiInputDevice(MidiInputDeviceJack* device) {
446            Config.GetConfigForUpdate().MidiDevice = device;
447            Config.SwitchConfig().MidiDevice = device;
448        }
449    #endif
450    
451        JackClient* JackClient::CreateAudio(String Name) { // static
452            JackClient* client;
453            std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
454            if (it == Clients.end()) {
455                client = new JackClient(Name);
456                Clients[Name] = client;
457            } else {
458                client = it->second;
459                if (client->audio) throw Exception("Jack audio device '" + Name + "' already exists");
460            }
461            client->audio = true;
462            return client;
463        }
464    
465        JackClient* JackClient::CreateMidi(String Name) { // static
466            JackClient* client;
467            std::map<String, JackClient*>::const_iterator it = Clients.find(Name);
468            if (it == Clients.end()) {
469                client = new JackClient(Name);
470                Clients[Name] = client;
471            } else {
472                client = it->second;
473                if (client->midi) throw Exception("Jack MIDI device '" + Name + "' already exists");
474            }
475            client->midi = true;
476            return client;
477        }
478    
479        void JackClient::ReleaseAudio(String Name) { // static
480            JackClient* client = Clients[Name];
481            client->SetAudioOutputDevice(0);
482            client->audio = false;
483            if (!client->midi) {
484                Clients.erase(Name);
485                delete client;
486            }
487        }
488    
489        void JackClient::ReleaseMidi(String Name) { // static
490            JackClient* client = Clients[Name];
491    #if HAVE_JACK_MIDI
492            client->SetMidiInputDevice(0);
493    #endif
494            client->midi = false;
495            if (!client->audio) {
496                Clients.erase(Name);
497                delete client;
498            }
499      }      }
500    
501  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.268  
changed lines
  Added in v.2512

  ViewVC Help
Powered by ViewVC