/* jack_miniFMsynth 1.1 by Matthias Nagorni */ /* This file is licensed under the GPL. */ #include #include #include #include #include #include #define POLY 10 #define GAIN 1.0 int signal_count; int midi_channel; snd_seq_t *seq_handle; unsigned long rate; jack_client_t *jack_handle; jack_port_t *output_port; char client_name[256]; double phi[POLY], phi_mod[POLY], pitch, modulation, velocity[POLY], attack, decay, sustain, release, env_time[POLY], env_level[POLY]; int harmonic, subharmonic, transpose, note[POLY], gate[POLY], note_active[POLY]; void sigterm_exit(int sig) { signal_count++; if (signal_count < 2) { printf("Closing JACK client.\n"); jack_client_close (jack_handle); snd_seq_close (seq_handle); } else { printf("JACK client closed.\n"); } exit(0); } double envelope(int *note_active, int gate, double *env_level, double t, double attack, double decay, double sustain, double release) { if (gate) { if (t > attack + decay) return(*env_level = sustain); if (t > attack) return(*env_level = 1.0 - (1.0 - sustain) * (t - attack) / decay); return(*env_level = t / attack); } else { if (t > release) { if (note_active) *note_active = 0; return(*env_level = 0); } return(*env_level * (1.0 - t / release)); } } int midi_callback() { snd_seq_event_t *ev; int l1; do { snd_seq_event_input(seq_handle, &ev); if ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0) && (ev->data.control.channel == midi_channel)) ev->type = SND_SEQ_EVENT_NOTEOFF; switch (ev->type) { case SND_SEQ_EVENT_PITCHBEND: pitch = (double)ev->data.control.value / 8192.0; break; case SND_SEQ_EVENT_CONTROLLER: if (ev->data.control.param == 1) { modulation = (double)ev->data.control.value / 10.0; } break; case SND_SEQ_EVENT_NOTEON: if (ev->data.control.channel == midi_channel) { for (l1 = 0; l1 < POLY; l1++) { if (!note_active[l1]) { note[l1] = ev->data.note.note; velocity[l1] = ev->data.note.velocity / 127.0; env_time[l1] = 0; gate[l1] = 1; note_active[l1] = 1; break; } } } break; case SND_SEQ_EVENT_NOTEOFF: if (ev->data.control.channel == midi_channel) { for (l1 = 0; l1 < POLY; l1++) { if (gate[l1] && note_active[l1] && (note[l1] == ev->data.note.note)) { env_time[l1] = 0; gate[l1] = 0; } } } break; } snd_seq_free_event(ev); } while (snd_seq_event_input_pending(seq_handle, 0) > 0); return (0); } int playback_callback(jack_nframes_t nframes, void *arg) { int l1, l2; double dphi, dphi_mod, f1, f2, f3, freq_note, sound, gain; gain = GAIN / (double)POLY; jack_default_audio_sample_t *buf = (jack_default_audio_sample_t *)jack_port_get_buffer(output_port, nframes); memset(buf, 0, nframes * sizeof(jack_default_audio_sample_t)); for (l2 = 0; l2 < POLY; l2++) { if (note_active[l2]) { f1 = 8.176 * exp((double)(transpose+note[l2]-2)*log(2.0)/12.0); f2 = 8.176 * exp((double)(transpose+note[l2])*log(2.0)/12.0); f3 = 8.176 * exp((double)(transpose+note[l2]+2)*log(2.0)/12.0); freq_note = (pitch > 0) ? f2 + (f3-f2)*pitch : f2 + (f2-f1)*pitch; dphi = M_PI * freq_note / (0.5 * (double)rate); dphi_mod = dphi * (double)harmonic / (double)subharmonic; for (l1 = 0; l1 < nframes; l1++) { phi[l2] += dphi; phi_mod[l2] += dphi_mod; if (phi[l2] > 2.0 * M_PI) phi[l2] -= 2.0 * M_PI; if (phi_mod[l2] > 2.0 * M_PI) phi_mod[l2] -= 2.0 * M_PI; sound = gain * envelope(¬e_active[l2], gate[l2], &env_level[l2], env_time[l2], attack, decay, sustain, release) * velocity[l2] * sin(phi[l2] + modulation * sin(phi_mod[l2])); env_time[l2] += 1.0 / (double)rate; buf[l1] += sound; } } } return(0); } snd_seq_t *open_seq() { snd_seq_t *seq_handle; int client_id, port_id; if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) { fprintf(stderr, "Error opening ALSA sequencer.\n"); exit(1); } snd_seq_set_client_name(seq_handle, "jack_miniFMsynth"); client_id = snd_seq_client_id(seq_handle); if (port_id = snd_seq_create_simple_port(seq_handle, "jack_miniFMsynth", SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION) < 0) { fprintf(stderr, "Error creating sequencer port.\n"); exit(1); } sprintf(client_name, "jack_miniFMsynth:%d:%d", client_id, port_id); return(seq_handle); } jack_client_t *open_jack() { jack_client_t *jack_handle; if ((jack_handle = jack_client_new(client_name)) == 0) { fprintf (stderr, "jack server not running ?\n"); exit (1); } output_port = jack_port_register(jack_handle, "Out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); jack_set_process_callback(jack_handle, playback_callback, 0); rate = jack_get_sample_rate(jack_handle); return(jack_handle); } int main (int argc, char *argv[]) { int seq_nfds, l1; struct pollfd *pfds; if (argc < 10) { fprintf(stderr, "jack_miniFMsynth \n"); exit(1); } midi_channel = atoi(argv[1]); modulation = atof(argv[2]); harmonic = atoi(argv[3]); subharmonic = atoi(argv[4]); transpose = atoi(argv[5]); attack = atof(argv[6]); decay = atof(argv[7]); sustain = atof(argv[8]); release = atof(argv[9]); pitch = 0; seq_handle = open_seq(); jack_handle = open_jack(); signal_count = 0; signal(SIGINT, sigterm_exit); signal(SIGTERM, sigterm_exit); seq_nfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN); pfds = (struct pollfd *)alloca(sizeof(struct pollfd) * seq_nfds); snd_seq_poll_descriptors(seq_handle, pfds, seq_nfds, POLLIN); for (l1 = 0; l1 < POLY; note_active[l1++] = 0); if (jack_activate(jack_handle)) { fprintf(stderr, "cannot activate client"); exit(1); } while (1) { if (poll (pfds, seq_nfds, 1000) > 0) { for (l1 = 0; l1 < seq_nfds; l1++) { if (pfds[l1].revents > 0) midi_callback(); } } } return (0); }