289 lines
7.5 KiB
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]);
|
|
}
|
|
}
|