/* ZynAddSubFX - a software synthesizer JackMultiEngine.cpp - Channeled Audio output JACK Copyright (C) 2012-2012 Mark McCurry Author: Mark McCurry 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include "Nio.h" #include "../Misc/Util.h" #include "../Misc/Master.h" #include "../Misc/Part.h" #include "../Misc/MiddleWare.h" #include "JackMultiEngine.h" extern zyncarla::MiddleWare *middleware; namespace zyncarla { using std::string; struct jack_multi { jack_port_t *ports[NUM_MIDI_PARTS * 2 + 2]; jack_client_t *client; bool running; }; JackMultiEngine::JackMultiEngine(const SYNTH_T &synth) :AudioOut(synth), impl(new jack_multi()) { impl->running = false; impl->client = NULL; name = "JACK-MULTI"; } JackMultiEngine::~JackMultiEngine(void) { delete impl; } void JackMultiEngine::setAudioEn(bool nval) { if(nval) Start(); else Stop(); } bool JackMultiEngine::getAudioEn() const { return impl->running; } bool JackMultiEngine::Start(void) { if(impl->client) return true; string clientname = "zynaddsubfx"; string postfix = Nio::getPostfix(); if(!postfix.empty()) clientname += "_" + postfix; if(Nio::pidInClientName) clientname += "_" + os_pid_as_padded_string(); jack_status_t jackstatus; impl->client = jack_client_open(clientname.c_str(), JackNullOption, &jackstatus); if(!impl->client) errx(1, "failed to connect to jack..."); //Create the set of jack ports char portName[20]; memset(portName,0,sizeof(portName)); #define JACK_REGISTER(x) jack_port_register(impl->client, x, \ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0) //Create the master wet port impl->ports[0] = JACK_REGISTER("out-L"); impl->ports[1] = JACK_REGISTER("out-R"); //Create all part's outputs for(int i = 0; i < NUM_MIDI_PARTS * 2; i += 2) { snprintf(portName, 19, "part%d/out-L", i / 2); impl->ports[2 + i] = JACK_REGISTER(portName); snprintf(portName, 19, "part%d/out-R", i / 2); impl->ports[3 + i] = JACK_REGISTER(portName); } //verify that all sample rate and buffer_size are the same in jack. //This insures that the connection can be made with no resampling or //buffering if(synth.samplerate != jack_get_sample_rate(impl->client)) errx(1, "jack must have the same sample rate!"); if(synth.buffersize != (int) jack_get_buffer_size(impl->client)) errx(1, "jack must have the same buffer size"); jack_set_process_callback(impl->client, _processCallback, this); //run if(jack_activate(impl->client)) errx(1, "failed at starting the jack client"); impl->running = true; return true; } int JackMultiEngine::_processCallback(jack_nframes_t nframes, void *arg) { return static_cast(arg)->processAudio(nframes); } int JackMultiEngine::processAudio(jack_nframes_t nframes) { //Gather all buffers float *buffers[NUM_MIDI_PARTS * 2 + 2]; for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) { //Abort if ports are only partially initialized if(!impl->ports[i]) return false; buffers[i] = (float *)jack_port_get_buffer(impl->ports[i], nframes); assert(buffers[i]); } //Get the wet samples from OutMgr Stereo smp = getNext(); memcpy(buffers[0], smp.l, synth.bufferbytes); memcpy(buffers[1], smp.r, synth.bufferbytes); //Gather other samples from individual parts Master &master = *middleware->spawnMaster(); for(int i = 0; i < NUM_MIDI_PARTS; ++i) { memcpy(buffers[2*i + 2], master.part[i]->partoutl, synth.bufferbytes); memcpy(buffers[2*i + 3], master.part[i]->partoutr, synth.bufferbytes); } return false; } void JackMultiEngine::Stop() { for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) { jack_port_t *port = impl->ports[i]; impl->ports[i] = NULL; if(port) jack_port_unregister(impl->client, port); } if(impl->client) jack_client_close(impl->client); impl->client = NULL; impl->running = false; } }