zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 70e0346ea40f655a48a277a3cdc234c718e3082b
parent 37d769d22e471952b2132eca5da7b58d4587d44c
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Fri, 18 Dec 2009 10:47:20 -0500

Nio: Added primitive JACK support in Nio

Diffstat:
Msrc/CMakeLists.txt | 3++-
Msrc/Nio/CMakeLists.txt | 11+++++------
Asrc/Nio/JackEngine.cpp | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Nio/JackEngine.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Nio/OutMgr.cpp | 10++++++++++
Msrc/UI/NioUI.cpp | 11++++++++++-
Msrc/UI/NioUI.h | 2+-
7 files changed, 329 insertions(+), 9 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -47,7 +47,7 @@ if (DefaultOutput STREQUAL alsa) elseif(DefaultOutput STREQUAL oss) add_definitions(-DOSS_DEFAULT=1) elseif(DefaultOutput STREQUAL jack) - add_definitions(-DOSS_DEFAULT=1) + add_definitions(-DJACK_DEFAULT=1) endif() @@ -60,6 +60,7 @@ if(AlsaEnable) add_definitions(-DALSA=1) endif(AlsaEnable) if(JackEnable) + list(APPEND AUDIO_LIBRARIES ${JACK_LIBRARIES}) add_definitions(-DJACK=1) endif(JackEnable) if(OssEnable) diff --git a/src/Nio/CMakeLists.txt b/src/Nio/CMakeLists.txt @@ -12,12 +12,11 @@ set(zynaddsubfx_nio_SRCS set(zynaddsubfx_nio_lib ) -#Uncomment when Jack is integrated -#if(JackEnable) -# include_directories(${JACK_INCLUDE_DIR}) -# list(APPEND zynaddsubfx_nio_SRCS JackEngine.cpp) -# set(zynaddsubfx_nio_lib ${JACK_LIBRARIES}) -#endif(JackEnable) +if(JackEnable) + include_directories(${JACK_INCLUDE_DIR}) + list(APPEND zynaddsubfx_nio_SRCS JackEngine.cpp) + list(APPEND zynaddsubfx_nio_lib ${JACK_LIBRARIES}) +endif(JackEnable) #Uncomment when Port Audio is integrated #if(PortAudioOutput) diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp @@ -0,0 +1,230 @@ +/* + JackEngine.cpp + + Copyright 2009, Alan Calvert + + This file is part of yoshimi, which is free software: you can + redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + yoshimi is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with yoshimi. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <iostream> + +//#include <jack/midiport.h> +#include <fcntl.h> +#include <sys/stat.h> + + +//#include "../Misc/Config.h" +#include "../Misc/Master.h" +#include "JackEngine.h" + +using namespace std; + +JackEngine::JackEngine(OutMgr *out) + :AudioOut(out), jackClient(NULL) +{ + audio.jackSamplerate = 0; + audio.jackNframes = 0; + for (int i = 0; i < 2; ++i) + { + audio.ports[i] = NULL; + audio.portBuffs[i] = NULL; + } +} + +bool JackEngine::connectServer(string server) +{ + cout << "Bla" << endl; + //temporary (move to a higher level configuation) + bool autostart_jack = true; + + + if (NULL == jackClient) // ie, not already connected + { + string clientname = "zynaddsubfx"; + jack_status_t jackstatus; + bool use_server_name = server.size() && server.compare("default") != 0; + jack_options_t jopts = (jack_options_t) + (((use_server_name) ? JackServerName : JackNullOption) + | ((autostart_jack) ? JackNullOption : JackNoStartServer)); + for (int tries = 0; tries < 3 && NULL == jackClient; ++tries) + { + if (use_server_name) + jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus, + server.c_str()); + else + jackClient = jack_client_open(clientname.c_str(), jopts, &jackstatus); + if (NULL != jackClient) + break; + else + usleep(3333); + } + if (NULL != jackClient) + return true; + else + cerr << "Error, failed to open jack client on server: " << server + << ", status " << jackstatus << endl; + return false; + } + return true; +} + +bool JackEngine::Start() +{ + enabled = true; + connectServer(""); + openAudio(); + if (NULL != jackClient) + { + int chk; + jack_set_error_function(_errorCallback); + jack_set_info_function(_infoCallback); + if ((chk = jack_set_xrun_callback(jackClient, _xrunCallback, this))) + cerr << "Error setting jack xrun callback" << endl; + if (jack_set_process_callback(jackClient, _processCallback, this)) + { + cerr << "Error, JackEngine failed to set process callback" << endl; + goto bail_out; + } + if (jack_activate(jackClient)) + { + cerr << "Error, failed to activate jack client" << endl;; + goto bail_out; + } + + return true; + } + else + cerr << "Error, NULL jackClient through Start()" << endl; +bail_out: + Stop(); + return false; +} + +void JackEngine::Stop() +{ + if (jackClient) + { + for (int i = 0; i < 2; ++i) + { + if (NULL != audio.ports[i]) + jack_port_unregister(jackClient, audio.ports[i]); + audio.ports[i] = NULL; + } + int chk = jack_client_close(jackClient); + jackClient = NULL; + } + enabled = false; +} + +bool JackEngine::openAudio() +{ + const char *portnames[] = { "left", "right" }; + for (int port = 0; port < 2; ++port) + { + audio.ports[port] = jack_port_register(jackClient, portnames[port], + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + } + if (NULL != audio.ports[0] && NULL != audio.ports[1]) + { + audio.jackSamplerate = jack_get_sample_rate(jackClient); + audio.jackNframes = jack_get_buffer_size(jackClient); + return true; + } + else + cerr << "Error, failed to register jack audio ports" << endl; + Stop(); + return false; +} + +int JackEngine::clientId() +{ + if (NULL != jackClient) + return jack_client_thread_id(jackClient); + else + return -1; +} + +string JackEngine::clientName() +{ + if (NULL != jackClient) + return string(jack_get_client_name(jackClient)); + else + cerr << "Error, clientName() with null jackClient" << endl; + return string("Oh, yoshimi :-("); +} + +int JackEngine::_processCallback(jack_nframes_t nframes, void *arg) +{ + return static_cast<JackEngine*>(arg)->processCallback(nframes); +} + +int JackEngine::processCallback(jack_nframes_t nframes) +{ + bool okaudio = true; + + if (NULL != audio.ports[0] && NULL != audio.ports[1]) + okaudio = processAudio(nframes); + return okaudio ? 0 : -1; +} + +bool JackEngine::processAudio(jack_nframes_t nframes) +{ + //cout << "I got called with: " << nframes << endl; + for (int port = 0; port < 2; ++port) + { + audio.portBuffs[port] = + (jsample_t*)jack_port_get_buffer(audio.ports[port], nframes); + if (NULL == audio.portBuffs[port]) + { + cerr << "Error, failed to get jack audio port buffer: " + << port << endl; + return false; + } + } + + Stereo<Sample> smp = getNext(); + //cout << "smp size of: " << smp.l().size() << endl; + memcpy(audio.portBuffs[0], smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE)); + memcpy(audio.portBuffs[1], smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE)); + //todo figure out how to do this right! + smp = getNext(); + memcpy(audio.portBuffs[0]+smp.l().size(), smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE)); + memcpy(audio.portBuffs[1]+smp.r().size(), smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE)); + smp = getNext(); + memcpy(audio.portBuffs[0]+2*smp.l().size(), smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE)); + memcpy(audio.portBuffs[1]+2*smp.r().size(), smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE)); + smp = getNext(); + memcpy(audio.portBuffs[0]+3*smp.l().size(), smp.l().c_buf(), smp.l().size()*sizeof(REALTYPE)); + memcpy(audio.portBuffs[1]+3*smp.r().size(), smp.r().c_buf(), smp.r().size()*sizeof(REALTYPE)); + + return true; + +} + +int JackEngine::_xrunCallback(void *arg) +{ + cerr << "Jack reports xrun" << endl; + return 0; +} + +void JackEngine::_errorCallback(const char *msg) +{ + cerr << "Jack reports error: " << msg << endl; +} + +void JackEngine::_infoCallback(const char *msg) +{ + cerr << "Jack info message: " << msg << endl; +} diff --git a/src/Nio/JackEngine.h b/src/Nio/JackEngine.h @@ -0,0 +1,71 @@ +/* + JackEngine.h + + Copyright 2009, Alan Calvert + + This file is part of yoshimi, which is free software: you can + redistribute it and/or modify it under the terms of the GNU General + Public License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + yoshimi is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with yoshimi. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef JACK_ENGINE_H +#define JACK_ENGINE_H + +#include <string> +#include <pthread.h> +#include <semaphore.h> +#include <jack/jack.h> +#include <jack/ringbuffer.h> + +#include "AudioOut.h" + +typedef jack_default_audio_sample_t jsample_t; + +class JackEngine : public AudioOut +{ + public: + JackEngine(OutMgr *out); + ~JackEngine() { }; + + bool setServer(std::string server); + bool Start(); + void Stop(); + + unsigned int getSamplerate() { return audio.jackSamplerate; }; + unsigned int getBuffersize() { return audio.jackNframes; }; + + std::string clientName(); + int clientId(); + + protected: + + int processCallback(jack_nframes_t nframes); + static int _processCallback(jack_nframes_t nframes, void *arg); + static void _errorCallback(const char *msg); + static void _infoCallback(const char *msg); + static int _xrunCallback(void *arg); + + private: + bool connectServer(std::string server); + bool openAudio(); + bool processAudio(jack_nframes_t nframes); + + jack_client_t *jackClient; + struct { + unsigned int jackSamplerate; + unsigned int jackNframes; + jack_port_t *ports[2]; + jsample_t *portBuffs[2]; + } audio; +}; + +#endif diff --git a/src/Nio/OutMgr.cpp b/src/Nio/OutMgr.cpp @@ -10,6 +10,9 @@ #if ALSA #include "AlsaEngine.h" #endif +#if JACK +#include "JackEngine.h" +#endif using namespace std; @@ -46,6 +49,13 @@ OutMgr::OutMgr(Master *nmaster) managedOuts["ALSA"] = new AlsaEngine(this); #endif #endif +#if JACK +#if JACK_DEFAULT + managedOuts["JACK"] = defaultOut = new JackEngine(this); +#else + managedOuts["JACK"] = new JackEngine(this); +#endif +#endif }; diff --git a/src/UI/NioUI.cpp b/src/UI/NioUI.cpp @@ -7,14 +7,23 @@ Pack::Pack(int x, int y, int w, int h) :Fl_Pack(x,y,w,h), b1(0,0,100,25,"Null I/O"), b2(0,0,100,25,"ALSA I/O"), - b3(0,0,100,25,"OSS I/O") + b3(0,0,100,25,"OSS I/O"), + b4(0,0,100,25,"JACK I/O") { b1.selection_color(fl_rgb_color(0,255,0)); b2.selection_color(fl_rgb_color(0,255,0)); b3.selection_color(fl_rgb_color(0,255,0)); + b4.selection_color(fl_rgb_color(0,255,0)); b1.callback(nioToggle, (void *)"NULL"); b2.callback(nioToggle, (void *)"ALSA"); b3.callback(nioToggle, (void *)"OSS"); + b4.callback(nioToggle, (void *)"JACK"); + //b1.value((NULL==sysOut->getOut("NULL")?false: + // sysOut->getOut("NULL")->isEnabled())); + //b2.value((NULL==sysOut->getOut("ALSA")?false: + // sysOut->getOut("ALSA")->isEnabled())); + //b3.value((NULL==sysOut->getOut("OSS")?false: + // sysOut->getOut("OSS")->isEnabled())); } void Pack::nioToggle(Fl_Widget *w, void *name) diff --git a/src/UI/NioUI.h b/src/UI/NioUI.h @@ -12,7 +12,7 @@ class Pack : public Fl_Pack public: Pack(int x, int y, int w, int h); private: - Fl_Light_Button b1,b2,b3; + Fl_Light_Button b1, b2, b3, b4; static void nioToggle(Fl_Widget *w, void *name); };