summaryrefslogtreecommitdiff
path: root/synth/jack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'synth/jack.cpp')
-rw-r--r--synth/jack.cpp467
1 files changed, 467 insertions, 0 deletions
diff --git a/synth/jack.cpp b/synth/jack.cpp
new file mode 100644
index 0000000..093e9b5
--- /dev/null
+++ b/synth/jack.cpp
@@ -0,0 +1,467 @@
+#include <string>
+#include <list>
+#include <iostream>
+#include <cstdlib>
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "defines.h"
+#include "globals.h"
+
+#include "jack.h"
+
+using namespace std;
+
+//#define DO_DEBUGGING_EVENTS
+
+jack_port_t *midi_in;
+jack_port_t *out_port[N_CHANNELS];
+#ifdef STEREO
+jack_port_t *out_port2[N_CHANNELS];
+#endif
+
+jack_client_t *jack_client = NULL;
+
+
+void maybe_calc_stuff() //TODO woandershinschieben? lfo.cpp oder so?
+{
+ static int lfocnt=0;
+ static int snhcnt=0;
+
+ if (lfocnt==0)
+ {
+ lfocnt=lfo_update_frames;
+
+ for (int i=0;i<N_LFOS;i++)
+ {
+ lfo_phase[i]=(lfo_phase[i]+1)%lfo_res[i];
+ curr_lfo[i]=lfo[i][lfo_phase[i]];
+ }
+ }
+
+ if (snhcnt==0)
+ {
+ snhcnt=sample_and_hold_frames;
+
+ //temp ranges between -ONE and ONE
+ fixed_t temp = (float(rand())/(RAND_MAX/2) - 1.0) * ONE;
+
+ for (int i=0;i<N_LFO_LEVELS;i++)
+ sample_and_hold[i]= temp*i/(N_LFO_LEVELS-1) + ONE;
+
+ curr_lfo[SNH_LFO]=sample_and_hold;
+ // could be moved to some init function, but looks clearer and
+ // does not eat up the cpu too much ;)
+ }
+
+ lfocnt--;
+ snhcnt--;
+}
+
+
+
+//connect to jack, init some stuff and get information
+void init_jack()
+{
+ jack_client = jack_client_open("flosoftsynth", JackNullOption, NULL);
+ if (jack_client == NULL)
+ throw string("Registering client failed");
+
+ if (jack_set_process_callback(jack_client, process_callback, 0))
+ throw string("Registering callback failed");
+
+ if (jack_set_xrun_callback(jack_client, xrun_callback, 0))
+ throw string("Registering xrun-callback failed");
+
+ midi_in = jack_port_register(jack_client, MIDI_IN_NAME,
+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+
+ if (midi_in == NULL)
+ throw string ("Registering MIDI IN failed");
+
+ for (int i=0;i<N_CHANNELS;i++)
+ {
+ #ifndef STEREO
+ out_port[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)).c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (out_port[i]==NULL)
+ throw string ("Registering some output port failed");
+ #else
+ out_port[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)+"L").c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ out_port2[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)+"R").c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ if ((out_port[i]==NULL) || (out_port2[i]==NULL))
+ throw string ("Registering some output port failed");
+ #endif
+ }
+
+ samp_rate=jack_get_sample_rate(jack_client);
+}
+
+
+//activate client and connect ports
+void start_jack(bool connect_audio_out, bool connect_midi_in)
+{
+ const char **ports;
+
+ if (jack_activate(jack_client))
+ throw string("Activating client failed");
+
+ if (connect_audio_out)
+ {
+ if ((ports = jack_get_ports (jack_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsPhysical|JackPortIsInput)) == NULL)
+ {
+ output_warning("WARNING: Could not find any physical playback ports. Leaving my ports\n"
+ " unconnected. You will not hear anything, but you can connect\n"
+ " them on your own. proceeding...");
+ }
+ else
+ {
+ #ifndef STEREO
+ int i=0;
+ while(ports[i]!=NULL)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[i]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+ i++;
+ }
+ #else
+ if (ports[1]==NULL)
+ {
+ output_note("NOTE: could not find two output ports. connecting to one, making everything\n"
+ " mono. this is not fatal, proceeding...");
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. this is not fatal, proceeding...");
+
+ if (jack_connect (jack_client, jack_port_name (out_port2[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. this is not fatal, proceeding...");
+ }
+ }
+ else
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+
+ if (jack_connect (jack_client, jack_port_name (out_port2[j]), ports[1]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+ }
+ }
+ #endif
+
+ free (ports);
+ }
+ }
+
+ if (connect_midi_in)
+ {
+ if ((ports = jack_get_ports (jack_client, NULL, JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsOutput)) == NULL)
+ {
+ output_warning("WARNING: Could not find any MIDI OUT ports. Leaving my MIDI IN port\n"
+ " unconnected. I cannot do anything unless you connect it to\n"
+ " some MIDI OUT port. proceeding...");
+ }
+ else
+ {
+ int i=0;
+ while(ports[i]!=NULL)
+ {
+ if (jack_connect (jack_client, ports[i],jack_port_name(midi_in)))
+ output_warning("WARNING: could not connect some MIDI OUT to my MIDI IN. this may or may not\n"
+ " result in being unable to receive any notes. you can still connect\n"
+ " the port manually. proceeding...");
+ i++;
+ }
+ free(ports);
+ }
+ }
+}
+
+void exit_jack()
+{
+ jack_deactivate(jack_client);
+ jack_client_close(jack_client);
+
+ jack_client=NULL;
+}
+
+int xrun_callback(void *notused)
+{
+ static list<float> history;
+
+ list<float>::iterator it;
+
+ float now=float(jack_get_time())/1000000;
+
+ cout << "got an XRUN! if this happens too often, consider reducing CPU usage, for\n example by setting a voice limit or by quitting other programs"<<endl<<endl;
+
+ history.push_back(now);
+
+ //erase all entries older than xrun_time
+ it=history.begin();
+ while (it!=history.end())
+ {
+ if (*it < now-xrun_time)
+ it=history.erase(it);
+ else
+ break;
+ }
+
+ if (history.size() >= xrun_n)
+ {
+ cout << "PANIC -- TOO MANY XRUNs! killing all voices" << endl<<endl;
+ for (int i=0;i<N_CHANNELS;i++)
+ channel[i]->panic();
+
+ history.clear();
+ }
+
+ return 0;
+}
+
+#define IGNORE_MIDI_OFFSET
+int process_callback(jack_nframes_t nframes, void *notused)
+{
+ #ifdef DO_DEBUGGING_EVENTS
+ static jack_nframes_t tmp=0, tmp2=0;
+ #endif
+
+ static jack_nframes_t next_cleanup=0;
+
+ size_t curr_event=0, n_events, i, chan;
+ void *inport;
+
+ jack_default_audio_sample_t *outbuf[N_CHANNELS];
+ #ifdef STEREO
+ jack_default_audio_sample_t *outbuf2[N_CHANNELS];
+ #endif
+
+ jack_midi_event_t event;
+ jack_nframes_t lastframe;
+ lastframe=jack_last_frame_time(jack_client);
+
+ if (nframes <= 0) {
+ output_note ("NOTE: Process callback called with nframes = 0; bug in JACK?");
+ return 0;
+ }
+
+ for (i=0;i<N_CHANNELS;i++)
+ {
+ outbuf[i]=(jack_default_audio_sample_t*) jack_port_get_buffer(out_port[i], nframes);
+ #ifdef STEREO
+ outbuf2[i]=(jack_default_audio_sample_t*) jack_port_get_buffer(out_port2[i], nframes);
+ #endif
+
+ if ( (outbuf[i]==NULL)
+ #ifdef STEREO
+ || (outbuf2[i]==NULL)
+ #endif
+ )
+ {
+ output_warning("WARNING: jack_port_get_buffer failed, cannot output anything.");
+ return 0;
+ }
+ }
+
+ inport = jack_port_get_buffer(midi_in, nframes);
+ if (inport == NULL)
+ {
+ output_warning("WARNING: jack_port_get_buffer failed, cannot receive anything.");
+ return 0;
+ }
+
+ n_events = jack_midi_get_event_count(inport /*, nframes */);
+
+ //as long as there are some events left and getting one fails, get the next
+ while ((n_events) && (jack_midi_event_get(&event, inport, curr_event /*, nframes */)))
+ {
+ output_note("NOTE: lost a note :(");
+ n_events--;
+ curr_event++;
+ }
+
+ if (lastframe>=next_cleanup)
+ {
+ next_cleanup=lastframe+cleanup_interval;
+ for (i=0;i<N_CHANNELS;i++)
+ channel[i]->cleanup();
+ }
+#ifdef DO_DEBUGGING_EVENTS
+ if (tmp==0) //DEBUG !!!
+ {
+ tmp=lastframe;
+ channel[0]->set_controller(5,10);
+ channel[0]->set_controller(65,127);
+
+ channel[0]->event(0x90,80,64);
+
+// channel[0]->event(0x90,84,64);
+ }
+ else if (tmp2==0)
+ {
+ if (lastframe>tmp+44100*2)
+ {
+ tmp2=1;
+ cout << "BÄÄM" << endl;
+ channel[0]->event(0x90,84,64);
+ }
+ }
+ else if (tmp2==1)
+ {
+ if (lastframe>tmp+44100*4)
+ {
+ tmp2=2;
+ channel[0]->event(0x90,87,5);
+ channel[0]->set_controller(57, 127);
+ cout << "BÄÄM2" << endl;
+ }
+ }
+ else
+ {
+ if (lastframe>tmp+44100*10)
+ {
+ cout << "finished" << endl;
+ exit(0);
+ }
+ }
+#endif
+
+#ifdef FRAMESKIP
+ if (outtemp_nframes_left)
+ {
+ jack_nframes_t real_nframes;
+ if (outtemp_nframes_left > nframes)
+ {
+ real_nframes=nframes;
+ outtemp_nframes_left-=nframes;
+ }
+ else
+ {
+ real_nframes=outtemp_nframes_left;
+ outtemp_nframes_left=0;
+ }
+
+ for (i=0;i<real_nframes;i++)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ outbuf[j][i]=outtemp[j];
+ #ifdef STEREO
+ outbuf2[j][i]=outtemp2[j];
+ #endif
+ }
+ }
+ }
+ else
+ i=0;
+
+ //begin where the above loop has stopped, at 0 if the loop wasn't
+ //executed
+ int upperbound=nframes-frameskip+1;
+ if (upperbound<0) upperbound=0;
+ for (i=i;i<upperbound;i+=frameskip)
+#else
+ for (i=0;i<nframes;i++)
+#endif
+ {
+ while ((n_events) && (i>=event.time))
+ {
+ output_verbose("processing event #"+IntToStr(curr_event)+" of "+IntToStr(n_events)+" events");
+ if (event.size > 3)
+ {
+ output_verbose(" Ignoring MIDI message longer than three bytes, probably a SysEx.");
+ }
+ else
+ {
+ chan=event.buffer[0] & 0x0F;
+ output_verbose(" channel="+IntToStr(chan)+", data is "+IntToStrHex(event.buffer[0])+" "+IntToStrHex(event.buffer[1])+" "+IntToStrHex(event.buffer[2]));
+
+ channel[chan]->event(event.buffer[0], event.buffer[1], event.buffer[2]);
+ }
+
+ n_events--;
+ curr_event++;
+
+ //as long as there are some events left and getting one fails, get the next
+ while ((n_events) && (jack_midi_event_get(&event, inport, curr_event /*, nframes */)))
+ {
+ output_note("NOTE: lost a note :(");
+ n_events--;
+ curr_event++;
+ }
+ }
+
+ maybe_calc_stuff();
+
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ #ifndef STEREO
+ outbuf[j][i]=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ #else
+ jack_default_audio_sample_t sample=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ outbuf[j][i]=channel[j]->balL*sample;
+ outbuf2[j][i]=channel[j]->balR*sample;
+ #endif // if the above changes, (1) must also change
+
+ #ifdef FRAMESKIP
+ for (size_t k=i+frameskip-1;k>i;k--)
+ {
+ outbuf[j][k]=outbuf[j][i];
+ #ifdef STEREO
+ outbuf2[j][k]=outbuf2[j][i];
+ #endif
+ }
+ #endif
+ }
+ }
+
+#ifdef FRAMESKIP
+ if (i!=nframes) // nicht aufgegangen?
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ { // (1)
+ #ifndef STEREO
+ outtemp[j]=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ #else
+ jack_default_audio_sample_t sample=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ outtemp[j]=channel[j]->balL*sample;
+ outtemp2[j]=channel[j]->balR*sample;
+ #endif
+ }
+
+ outtemp_nframes_left=frameskip-nframes+i;
+
+ for (i=i; i<nframes; i++)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ outbuf[j][i]=outtemp[j];
+ #ifdef STEREO
+ outbuf2[j][i]=outtemp2[j];
+ #endif
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+