--- linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp 2005/04/10 11:55:44 497 +++ linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp 2013/02/02 18:52:15 2410 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 Christian Schoenebeck * + * Copyright (C) 2005 - 2013 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -66,11 +66,11 @@ } std::vector AudioOutputDeviceJack::AudioChannelJack::ParameterJackBindings::PossibilitiesAsString() { - 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); if (!pPortNames) return std::vector(); std::vector result; for (int i = 0; pPortNames[i]; i++) result.push_back(pPortNames[i]); - //free(pPortNames); FIXME: pPortNames should be freed here + free(pPortNames); return result; } @@ -105,6 +105,7 @@ AudioOutputDeviceJack::AudioChannelJack::AudioChannelJack(uint ChannelNr, AudioOutputDeviceJack* pDevice) throw (AudioOutputException) : AudioChannel(ChannelNr, CreateJackPort(ChannelNr, pDevice), pDevice->uiMaxSamplesPerCycle) { this->pDevice = pDevice; this->ChannelNr = ChannelNr; + delete Parameters["NAME"]; Parameters["NAME"] = new ParameterName(this); Parameters["JACK_BINDINGS"] = new ParameterJackBindings(this); } @@ -119,6 +120,12 @@ if (!hJackPort) throw AudioOutputException("Jack: Cannot register Jack output port."); return (float*) jack_port_get_buffer(hJackPort, pDevice->uiMaxSamplesPerCycle); } + + void AudioOutputDeviceJack::AudioChannelJack::UpdateJackBuffer(uint size) { + SetBuffer( + (float*)jack_port_get_buffer(hJackPort, size) + ); + } @@ -129,7 +136,7 @@ InitWithDefault(); // use default name } - AudioOutputDeviceJack::ParameterName::ParameterName(String s) throw (LinuxSamplerException) : DeviceCreationParameterString(s) { + AudioOutputDeviceJack::ParameterName::ParameterName(String s) throw (Exception) : DeviceCreationParameterString(s) { } String AudioOutputDeviceJack::ParameterName::Description() { @@ -156,7 +163,7 @@ return (existingJackDevices) ? "LinuxSampler" + ToString(existingJackDevices) : "LinuxSampler"; } - void AudioOutputDeviceJack::ParameterName::OnSetValue(String s) throw (LinuxSamplerException) { + void AudioOutputDeviceJack::ParameterName::OnSetValue(String s) throw (Exception) { // not possible, as parameter is fix } @@ -177,19 +184,10 @@ * @see AcquireChannels() */ AudioOutputDeviceJack::AudioOutputDeviceJack(std::map Parameters) : AudioOutputDevice(Parameters) { - if (((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString().size() >= jack_client_name_size()) - throw LinuxSamplerException("JACK client name too long"); - - if ((hJackClient = jack_client_new(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString().c_str())) == 0) - throw AudioOutputException("Seems Jack server not running."); - + JackClient* pJackClient = JackClient::CreateAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString()); existingJackDevices++; - - jack_set_process_callback(hJackClient, __libjack_process_callback, this); - jack_on_shutdown(hJackClient, __libjack_shutdown_callback, this); - if (jack_activate(hJackClient)) - throw AudioOutputException("Jack: Cannot activate Jack client."); - + pJackClient->SetAudioOutputDevice(this); + hJackClient = pJackClient->hJackClient; uiMaxSamplesPerCycle = jack_get_buffer_size(hJackClient); // create audio channels @@ -200,8 +198,8 @@ } AudioOutputDeviceJack::~AudioOutputDeviceJack() { - // destroy jack client - jack_client_close(hJackClient); + // destroy jack client if there is no midi device associated with it + JackClient::ReleaseAudio(((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString()); existingJackDevices--; } @@ -210,14 +208,34 @@ * libjack to demand transmission of further sample points. */ int AudioOutputDeviceJack::Process(uint Samples) { + int res; + + // in recent versions of JACK2 and JACk1, we are forced to + // re-retrieve the audio buffer pointers in each process period + UpdateJackBuffers(Samples); + if (csIsPlaying.Pop()) { // let all connected engines render 'Samples' sample points - return RenderAudio(Samples); + res = RenderAudio(Samples); } else { // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio - return RenderSilence(Samples); + res = RenderSilence(Samples); } + csIsPlaying.RttDone(); + return res; + } + + void AudioOutputDeviceJack::UpdateJackBuffers(uint size) { + for (int i = 0; i < Channels.size(); ++i) + static_cast(Channels[i])->UpdateJackBuffer(size); + } + + float AudioOutputDeviceJack::latency() { + if (!hJackClient) return -1; + const float size = jack_get_buffer_size(hJackClient); + const float rate = jack_get_sample_rate(hJackClient); + return size / rate; } void AudioOutputDeviceJack::Play() { @@ -257,22 +275,164 @@ } String AudioOutputDeviceJack::Version() { - String s = "$Revision: 1.19 $"; + String s = "$Revision: 1.25 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } + + +// *************** JackClient *************** +// * + // libjack callback functions - int __libjack_process_callback(jack_nframes_t nframes, void* arg) { - AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg; - return pAudioOutputDeviceJack->Process(nframes); + int linuxsampler_libjack_process_callback(jack_nframes_t nframes, void* arg) { + return static_cast(arg)->Process(nframes); } - void __libjack_shutdown_callback(void* arg) { - AudioOutputDeviceJack* pAudioOutputDeviceJack = (AudioOutputDeviceJack*) arg; - pAudioOutputDeviceJack->Stop(); + void linuxsampler_libjack_shutdown_callback(void* arg) { + static_cast(arg)->Stop(); fprintf(stderr, "Jack: Jack server shutdown, exiting.\n"); } + + int JackClient::libjackSampleRateCallback(jack_nframes_t nframes, void *arg) { + JackClient* client = static_cast(arg); + const config_t& config = client->ConfigReader.Lock(); + if (config.AudioDevice) + config.AudioDevice->ReconnectAll(); + client->ConfigReader.Unlock(); + return 0; + } + + int JackClient::libjackBufferSizeCallback(jack_nframes_t nframes, void *arg) { + dmsg(1,("libjackBufferSizeCallback(%d)\n",nframes)); + JackClient* client = static_cast(arg); + const config_t& config = client->ConfigReader.Lock(); + if (config.AudioDevice) { + config.AudioDevice->UpdateJackBuffers(nframes); + config.AudioDevice->ReconnectAll(); + } + client->ConfigReader.Unlock(); + return 0; + } + + std::map JackClient::Clients; + + int JackClient::Process(uint Samples) { + const config_t& config = ConfigReader.Lock(); +#if HAVE_JACK_MIDI + if (config.MidiDevice) config.MidiDevice->Process(Samples); +#endif + int res = config.AudioDevice ? config.AudioDevice->Process(Samples) : 0; + ConfigReader.Unlock(); + return res; + } + + void JackClient::Stop() { + const config_t& config = ConfigReader.Lock(); + if (config.AudioDevice) config.AudioDevice->Stop(); + ConfigReader.Unlock(); + } + + JackClient::JackClient(String Name) : ConfigReader(Config) { + { + config_t& config = Config.GetConfigForUpdate(); + config.AudioDevice = 0; +#if HAVE_JACK_MIDI + config.MidiDevice = 0; +#endif + } + { + config_t& config = Config.SwitchConfig(); + config.AudioDevice = 0; +#if HAVE_JACK_MIDI + config.MidiDevice = 0; +#endif + } + audio = midi = false; + if (Name.size() >= jack_client_name_size()) + throw Exception("JACK client name too long"); +#if HAVE_JACK_CLIENT_OPEN + hJackClient = jack_client_open(Name.c_str(), JackNullOption, NULL); +#else + hJackClient = jack_client_new(Name.c_str()); +#endif + if (!hJackClient) + throw Exception("Seems Jack server is not running."); + jack_set_process_callback(hJackClient, linuxsampler_libjack_process_callback, this); + jack_on_shutdown(hJackClient, linuxsampler_libjack_shutdown_callback, this); + jack_set_buffer_size_callback(hJackClient, libjackBufferSizeCallback, this); + jack_set_sample_rate_callback(hJackClient, libjackSampleRateCallback, this); + + if (jack_activate(hJackClient)) + throw Exception("Jack: Cannot activate Jack client."); + } + + JackClient::~JackClient() { + jack_client_close(hJackClient); + } + + void JackClient::SetAudioOutputDevice(AudioOutputDeviceJack* device) { + Config.GetConfigForUpdate().AudioDevice = device; + Config.SwitchConfig().AudioDevice = device; + } + +#if HAVE_JACK_MIDI + void JackClient::SetMidiInputDevice(MidiInputDeviceJack* device) { + Config.GetConfigForUpdate().MidiDevice = device; + Config.SwitchConfig().MidiDevice = device; + } +#endif + + JackClient* JackClient::CreateAudio(String Name) { // static + JackClient* client; + std::map::const_iterator it = Clients.find(Name); + if (it == Clients.end()) { + client = new JackClient(Name); + Clients[Name] = client; + } else { + client = it->second; + if (client->audio) throw Exception("Jack audio device '" + Name + "' already exists"); + } + client->audio = true; + return client; + } + + JackClient* JackClient::CreateMidi(String Name) { // static + JackClient* client; + std::map::const_iterator it = Clients.find(Name); + if (it == Clients.end()) { + client = new JackClient(Name); + Clients[Name] = client; + } else { + client = it->second; + if (client->midi) throw Exception("Jack MIDI device '" + Name + "' already exists"); + } + client->midi = true; + return client; + } + + void JackClient::ReleaseAudio(String Name) { // static + JackClient* client = Clients[Name]; + client->SetAudioOutputDevice(0); + client->audio = false; + if (!client->midi) { + Clients.erase(Name); + delete client; + } + } + + void JackClient::ReleaseMidi(String Name) { // static + JackClient* client = Clients[Name]; +#if HAVE_JACK_MIDI + client->SetMidiInputDevice(0); +#endif + client->midi = false; + if (!client->audio) { + Clients.erase(Name); + delete client; + } + } } // namespace LinuxSampler