This repository has been archived on 2017-07-22. You can view files and clone it, but cannot push or open issues/pull-requests.
jlsampler/sampler.c

289 lines
7.5 KiB
C

#include <alsa/asoundlib.h>
#include <math.h>
#include <pthread.h>
#include "jl_mem.h"
#include "jl_ringbuf.h"
#include "sampler.h"
/****************
* sampler_main *
****************/
static void proc_midi_event(Sampler *s, MidiEvt *evt) {
if(evt->type == MIDI_EVT_NOTE) {
int num_triggers = s->num_triggers[evt->note];
for(int i = 0; i < num_triggers; ++i) {
int idx = s->trigger_map[evt->note][i];
for(int j = 0; j < s->num_cuts[idx]; ++j) {
sound_cut(&s->sounds[s->cut_map[idx][j]]);
}
sound_trigger(&s->sounds[idx], evt->velocity);
}
} else if(evt->type == MIDI_EVT_CTRL) {
if(evt->control == s->sustainControl) {
gl_sustain = evt->value > 0.5;
}
}
}
static void proc_midi(Sampler *s) {
int num_events = jlrb_count(s->midi_new);
for(int i = 0; i < num_events; ++i) {
MidiEvt *evt = jlrb_get(s->midi_new);
proc_midi_event(s, evt);
jlrb_put(s->midi_free, evt);
}
}
static int jack_process(jack_nframes_t nframes, void *data) {
Sampler *s = data;
if(nframes != s->buf_size) {
printf("Buffer size changed.\n");
exit(1);
}
// Processing incoming midi events.
proc_midi(s);
// Get output port arrays.
float *L = jack_port_get_buffer(s->jack_port_L, nframes);
float *R = jack_port_get_buffer(s->jack_port_R, nframes);
// Zero the output buffers.
memset(L, 0, nframes * sizeof(float));
memset(R, 0, nframes * sizeof(float));
// Write output from each sound.
int count = s->num_sounds;
for(int i = 0; i < count; ++i) {
Sound *sound = &s->sounds[i];
if(sound->num_playing == 0) {
continue;
}
sound_write_buffer(sound, L, R);
}
for(int i = 0; i < nframes; ++i) {
if(L[i] > s->L_max) {
s->L_max = L[i];
} else if (-L[i] > s->L_max) {
s->L_max = -L[i];
}
if(R[i] > s->R_max) {
s->R_max = R[i];
} else if (-R[i] > s->R_max) {
s->R_max = -R[i];
}
}
return 0;
}
static double calc_velocity(Sampler *s, int v_in) {
if(v_in <= s->midi_min_vel) {
return 0;
} else if(v_in >= s->midi_max_vel) {
return 1;
}
double vel = v_in;
vel -= s->midi_min_vel;
vel /= s->midi_max_vel - s->midi_min_vel;
return vel;
}
void midi_loop(Sampler *s) {
// We need to open the sequencer before doing anything else.
snd_seq_t *handle;
int status = snd_seq_open(&handle, "default", SND_SEQ_OPEN_INPUT, 0);
if (status != 0) {
printf("Failed to open sequencer.\n");
exit(1);
}
// Give the client a name.
status = snd_seq_set_client_name(handle, "JLSampler");
if (status != 0) {
printf("Failed to set sequencer client name.\n");
exit(1);
}
// Create a port with write capabilities.
int caps = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
int portNum = snd_seq_create_simple_port(
handle, "MIDI In", caps, SND_SEQ_PORT_TYPE_MIDI_GM);
if (portNum < 0) {
printf("Failed to create sequencer port.\n");
exit(1);
}
// We need a midi event handle to read incoming midi events.
snd_seq_event_t *seq_event;
MidiEvt *event;
while(1) {
status = snd_seq_event_input(handle, &seq_event);
if (status < 0) {
printf("Sampler: Failed to read MIDI event. Status: %i\n", status);
continue;
}
// Skip events that we won't process.
if(seq_event->type != SND_SEQ_EVENT_NOTEON &&
seq_event->type != SND_SEQ_EVENT_NOTEOFF &&
seq_event->type != SND_SEQ_EVENT_CONTROLLER) {
continue;
}
event = jlrb_get(s->midi_free);
if(event == NULL) {
printf("No free midi events available.\n");
continue;
}
switch (seq_event->type) {
case SND_SEQ_EVENT_NOTEON:
event->type = MIDI_EVT_NOTE;
event->note = seq_event->data.note.note;
event->velocity = calc_velocity(s, seq_event->data.note.velocity);
break;
case SND_SEQ_EVENT_NOTEOFF:
event->type = MIDI_EVT_NOTE;
event->note = seq_event->data.note.note;
event->velocity = 0;
break;
case SND_SEQ_EVENT_CONTROLLER:
event->type = MIDI_EVT_CTRL;
event->control = seq_event->data.control.param;
event->value = (double)(seq_event->data.control.value) / 127.0;
break;
}
jlrb_put(s->midi_new, event);
}
}
static void print_meter(float val) {
val = (log10(val) + 3.0) / 3.0;
for(int j = 0; j < 64; ++j) {
if(val > (float)j/64.0) {
printf("-");
} else {
printf(" ");
}
}
printf("|");
for(int j = 0; j < 8; ++j) {
if(val > 1.0 + (float)j/8.0) {
printf("=");
} else {
printf(" ");
}
}
printf("|\n");
}
static void meter_loop(void *data) {
Sampler *s = data;
struct timespec sleep_time;
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = 100000000;
while(1) {
nanosleep(&sleep_time, NULL);
printf("\n\n");
printf("L ");
print_meter(s->L_max);
s->L_max = 0;
printf("R ");
print_meter(s->R_max);
s->R_max = 0;
}
}
void sampler_main(Sampler *s) {
// Initialize JACK client.
printf("Creating JACK client...\n");
s->jack_client = jack_client_open("JLSampler", JackNullOption, NULL);
// Create jack output ports.
printf("Creating JACK output ports...\n");
s->jack_port_L = jack_port_register(s->jack_client, "Out_1",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
s->jack_port_R = jack_port_register(s->jack_client, "Out_2",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
// Get the jack buffer size.
printf("Getting JACK buffer size...\n");
s->buf_size = (int)jack_get_buffer_size(s->jack_client);
printf(" Got %i\n", s->buf_size);
if(s->buf_size % 8 != 0) {
printf("Buffer size must be a multiple of 8.\n");
exit(1);
}
// Load samples.
#pragma omp parallel for
for(int i = 0; i < s->num_sounds; ++i) {
sound_load_samples(&s->sounds[i], s->buf_size);
}
// Register the jack callback function.
printf("Setting JACK callback...\n");
jack_set_process_callback(s->jack_client, jack_process, (void *)s);
printf("Activating JACK client...\n");
jack_activate(s->jack_client);
// Start the VU meter loop in the background.
pthread_t th;
pthread_create(&th, NULL, (void *) &meter_loop, s);
// Collect midi events in the main thread.
printf("Starting midi loop...\n");
midi_loop(s);
}
/****************
* sampler_free *
****************/
void sampler_free(Sampler *sampler) {
int n = jlrb_count(sampler->midi_free);
for(int i = 0; i < n; ++i) {
MidiEvt *evt = jlrb_get(sampler->midi_free);
jl_free(evt);
}
jlrb_free(&sampler->midi_free);
n = jlrb_count(sampler->midi_new);
for(int i = 0; i < n; ++i) {
MidiEvt *evt = jlrb_get(sampler->midi_new);
jl_free(evt);
}
jlrb_free(&sampler->midi_new);
for(int i = 0; i < MAX_SOUNDS; ++i) {
sound_free(&sampler->sounds[i]);
}
}