#include #include #include #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]); } }