--- linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp 2005/03/21 23:40:56 483 +++ linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceJack.cpp 2008/09/06 14:55:18 1764 @@ -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 - 2008 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); } @@ -129,7 +130,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 +157,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 +178,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 +192,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 +202,17 @@ * libjack to demand transmission of further sample points. */ int AudioOutputDeviceJack::Process(uint Samples) { + int res; 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::Play() { @@ -225,7 +220,7 @@ } bool AudioOutputDeviceJack::IsPlaying() { - csIsPlaying.GetUnsafe(); + return csIsPlaying.GetUnsafe(); } void AudioOutputDeviceJack::Stop() { @@ -257,23 +252,141 @@ } String AudioOutputDeviceJack::Version() { - String s = "$Revision: 1.18 $"; + 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"); } + 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); + 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 #endif // HAVE_JACK