Docs corrections around required 0- mark
[quincer.git] / src / realtime.c
blob786292a26bab74eaa35c99acfec3fc3eabbf6eda
1 #include <jack/jack.h>
2 #include <jack/midiport.h>
3 #include "context.h"
4 #include "realtime.h"
5 #include <string.h>
6 #include <stdio.h>
8 static int get_effective_bpm(qn_pattern_t* pat, int base_beats_per_minute) {
9 int bpm = base_beats_per_minute;
10 if(pat->bpm_relation == RelativeToBase && bpm+pat->beats_per_minute > 0) {
11 bpm += pat->beats_per_minute;
13 return bpm;
16 static void set_playheads(qn_context_t* ctx, int pattern_advanced, enum eventset evtset) {
18 qn_pattern_t *start_pattern = NULL, *newest_pattern = NULL;
19 switch(evtset) {
20 case PREFIX:
21 start_pattern = newest_pattern = ctx->next_pattern_func(ctx);
22 break;
23 case CURRENT:
24 start_pattern = newest_pattern = ctx->run->curr;
25 if(pattern_advanced) {
26 newest_pattern = ctx->next_pattern_func(ctx);
28 break;
29 case SUFFIX:
30 start_pattern = newest_pattern = ctx->run->prev;
31 if(pattern_advanced) {
32 start_pattern = NULL;
34 break;
35 case NEVENTSETS:
36 break;
39 int k;
40 if(pattern_advanced && start_pattern) {
41 // Reset playhead to 0 for the past pattern. If the
42 // newest pattern is the same as the old one, the next
43 // block will overwrite the 0 with playhead_tmp
44 for(k=0;k<start_pattern->npatternvoices;k++) {
45 qn_patternvoice_t* pv = start_pattern->patternvoices + k;
46 pv->playhead[evtset] = 0;
50 // Set playhead for the newest pattern.
51 if(newest_pattern) {
52 for(k=0;k<newest_pattern->npatternvoices;k++) {
53 qn_patternvoice_t* pv = newest_pattern->patternvoices + k;
54 pv->playhead[evtset] = pv->playhead_tmp[evtset];
59 static void accumulate_pattern_events(qn_context_t* ctx, jack_port_t* port,
60 qn_pattern_t* pat, int* nout, qn_outevent_t* out,
61 jack_nframes_t fill_last_nframes,
62 int* pattern_advanced, enum eventset evtset) {
63 if(!pat) return;
64 qn_runstate_t* run = ctx->run;
66 // If we straddle two patterns, the latter one will only
67 // want to fill a subset of frames with events.
68 while(fill_last_nframes) {
69 // Inside this loop assumes we're working within a single pattern
70 // (pat). If we advance to a new pattern, we advance this loop and
71 // reduce fill_last_nframes.
73 int k;
74 for(k=0;k<pat->npatternvoices;k++) {
75 qn_patternvoice_t* pv = pat->patternvoices + k;
76 pv->playhead_tmp[evtset] = pv->playhead[evtset];
79 int curr_bpm = get_effective_bpm(run->curr,run->base_beats_per_minute);
80 int this_bpm = get_effective_bpm(pat,run->base_beats_per_minute);
81 int curr_beat_len = (run->sample_rate * 60) / curr_bpm;
82 int this_beat_len = (run->sample_rate * 60) / this_bpm;
84 // When the longest pv completes, the pattern advances from curr
85 // to next.
86 jack_nframes_t til_curr_pattern_end =
87 (run->curr->longest_pv->nbeats * curr_beat_len)
88 - run->curr->longest_pv->playhead[CURRENT];
90 int j;
91 jack_nframes_t fill_last_nframes_in_pv;
92 for(j=0; j<pat->npatternvoices; j++) {
93 fill_last_nframes_in_pv = fill_last_nframes;
94 qn_patternvoice_t* pv = pat->patternvoices + j;
96 int pv_len = pv->nbeats * this_beat_len;
97 int pv_end = pv_len;
99 accum_one_pv:
100 if(evtset == PREFIX) {
101 qn_patternvoice_t *current = run->curr->longest_pv;
102 pv_end = 0;
104 jack_nframes_t longest_len = current->nbeats * ((run->sample_rate * 60) / (get_effective_bpm(run->curr,run->base_beats_per_minute)));
105 int longest_playhead = current->playhead[CURRENT];
107 pv->playhead_tmp[PREFIX] = longest_playhead - longest_len;
108 } else if(evtset == SUFFIX) {
109 if(run->prev) {
110 qn_patternvoice_t *current = run->curr->longest_pv;
111 jack_nframes_t longest_len = run->prev->longest_pv->nbeats *
112 ((run->sample_rate * 60) /
113 (get_effective_bpm(run->prev,run->base_beats_per_minute)));
115 int longest_playhead = current->playhead[CURRENT];
116 pv_end = pv_len + longest_playhead;
118 setting_suffix_playheadtmp:
119 pv->playhead_tmp[SUFFIX] = longest_len + longest_playhead;
120 } else {
121 // Nothing to do
122 return;
126 int range_start = pv->playhead_tmp[evtset];
127 int range_end = range_start + fill_last_nframes;
129 // Don't collect suffix events in current pattern
130 if(fill_last_nframes > til_curr_pattern_end) {
131 range_end = range_start + til_curr_pattern_end;
134 int maybe_more_loops = 1;
135 while(maybe_more_loops) {
136 if(pv->voice->port == port) {
137 int k;
138 for(k=0; k<pv->nevents; k++) {
139 qn_event_t ev = pv->events[k];
141 // evtime is expressing a value as a frame index.
142 // However, jack_nframes_t is unsigned and we need to
143 // support negative indices (prefixes).
144 int evtime = (this_beat_len * ev.start_tick)
145 / pat->tbm_ticks_per_beat;
147 // Enforce some space between note off and subsequent
148 // note on, otherwise the later note may not sound
149 // articulated
150 # define NOTE_OFF_EARLY_NFRAMES (this_beat_len/8)
151 if((ev.data[0] & 0xf0) == 0x80) {
152 evtime -= NOTE_OFF_EARLY_NFRAMES;
153 if (evtime < 0 && evtset != PREFIX) {
154 evtime += pv_len;
157 # define MAX_OUT_EVENTS 1024
159 if(evtime >= range_start
160 && evtime < range_end
161 && *nout < MAX_OUT_EVENTS) {
163 qn_outevent_t* outev = out + *nout;
165 // time in the out event must be indexed
166 // with 0 being the start of nframes
167 outev->at = evtime - pv->playhead_tmp[evtset];
169 outev->data[0] = ev.data[0];
170 outev->data[1] = ev.data[1];
171 outev->data[2] = ev.data[2];
173 switch(ev.data[0] & 0xf0) {
174 case 0x90: // note on
175 case 0x80: // note off
176 case 0xb0: // control change
177 outev->data[0] |= pv->voice->channel;
178 break;
181 increment_nout:
182 *nout = *nout+1;
185 } // if(pv->voice->port == port)
187 inner_loop_end:
188 if(range_end >= pv_end && evtset==CURRENT) {
189 // We are straddling the boundary of this loop and something.
191 if(fill_last_nframes > til_curr_pattern_end) {
192 // This is the end of the pattern, and
193 // We're straddling two patterns
194 maybe_more_loops = 0;
195 *pattern_advanced = 1;
196 } else {
197 // Loop this patternvoice because there's
198 // still a longer one in progress
199 range_start = pv_end - pv_len;
200 range_end -= pv_end;
201 pv->playhead_tmp[evtset] = range_start;
204 fill_last_nframes_in_pv = range_end - pv_end;
206 } else {
207 // Simple case, this patternvoice didn't wrap
208 maybe_more_loops = 0;
209 fill_last_nframes_in_pv = 0;
210 if(evtset==CURRENT) {
211 pv->playhead_tmp[evtset] = range_end;
214 } // while(maybe_more_loops)
215 } // loop patternvoices
217 save_out_nframes:
218 fill_last_nframes = fill_last_nframes_in_pv;
220 if(*pattern_advanced && evtset==CURRENT) {
221 pat = ctx->next_pattern_func(ctx);
223 if(pat) {
224 for(k=0;k<pat->npatternvoices;k++) {
225 qn_patternvoice_t* pv = pat->patternvoices + k;
226 pv->playhead_tmp[evtset] = 0;
230 } // while(fill_last_nframes)
233 int process(jack_nframes_t nframes, void *arg) {
234 qn_context_t* ctx = (qn_context_t*)arg;
235 qn_runstate_t* run = ctx->run;
237 // Clear all port buffers. Jack doesn't clear them
238 // between calls
239 int i;
240 for(i=0; i<ctx->config->noutports; i++) {
241 jack_port_t* port = run->ports[i];
242 void* port_buf = jack_port_get_buffer( port, nframes);
243 jack_midi_clear_buffer(port_buf);
246 jack_transport_state_t transport = jack_transport_query(run->client, NULL);
248 // If stopping, send "All Notes Off" MIDI message
249 if(transport != run->transtate) {
250 run->transtate = transport;
251 if(transport == JackTransportStopped) {
252 for(i=0; i<ctx->config->noutports; i++) {
253 jack_port_t* port = run->ports[i];
255 int channels_encountered[16];
256 int ce;
257 for(ce=0;ce<16;ce++) {
258 channels_encountered[ce] = 0;
261 void* port_buf = jack_port_get_buffer( port, nframes);
262 jack_midi_clear_buffer(port_buf);
264 int j;
265 for(j=0; j<ctx->config->nvoices; j++) {
266 qn_voice_t* voice = ctx->config->voices + j;
267 if(voice->port == port) {
269 // Don't write multiple messages per-channel per-port
270 if(channels_encountered[voice->channel]) continue;
272 channels_encountered[voice->channel] = 1;
274 unsigned char val[NBYTES_MIDI];
275 val[0] = (176 | voice->channel);
276 val[1] = 123;
277 val[2] = 0;
278 if(jack_midi_event_write(port_buf,0,val,NBYTES_MIDI)) {
279 printf("Couldn't reserve space for a note off event.\n");
287 if(transport != JackTransportRolling) {
288 return 0;
291 int pattern_advanced = 0;
293 // Write a list of output events, grouped by output port
294 for(i=0; i<ctx->config->noutports; i++) {
295 jack_port_t* port = run->ports[i];
297 // We can't write events to the actual port_buf yet since they
298 // would be out of order. Use a fixed buffer for our intermediate
299 // event store.
300 int nout = 0;
301 qn_outevent_t out[MAX_OUT_EVENTS];
303 accumulate_pattern_events(ctx,port,ctx->next_pattern_func(ctx),
304 &nout,out,nframes,&pattern_advanced, PREFIX);
305 accumulate_pattern_events(ctx,port,run->prev,
306 &nout,out,nframes,&pattern_advanced, SUFFIX);
307 accumulate_pattern_events(ctx,port,run->curr,
308 &nout,out,nframes,&pattern_advanced, CURRENT);
310 if(nout) {
312 qsort(out,nout,sizeof(qn_outevent_t),cmp_outevent);
314 void* port_buf = jack_port_get_buffer( port, nframes);
315 jack_midi_clear_buffer(port_buf);
317 jack_midi_data_t* buffer;
318 int j;
319 for(j=0; j<nout; j++) {
320 qn_outevent_t* evt = out + j;
321 reserve_out_event:
322 buffer = jack_midi_event_reserve(port_buf, evt->at, NBYTES_MIDI);
323 if(buffer) {
324 memcpy(buffer,evt->data,NBYTES_MIDI);
325 } else {
326 printf("Couldn't send event, probably the time %d is wrong.\n",
327 evt->at);
331 } // loop noutports
333 set_playheads(ctx,pattern_advanced,PREFIX);
334 set_playheads(ctx,pattern_advanced,SUFFIX);
335 set_playheads(ctx,pattern_advanced,CURRENT);
337 // Calculate bar/beat/tick since we are a jack timebase master.
338 // It's easier to do this before we advance the pattern than
339 // to do it in timebase() where we have to look backwards.
340 jack_nframes_t frames_left = nframes;
341 qn_pattern_t* pat = run->curr;
342 while(frames_left && pat) {
343 int bpm = get_effective_bpm(pat,run->base_beats_per_minute);
344 jack_nframes_t tick_len = (run->sample_rate * 60) / (bpm * pat->tbm_ticks_per_beat);
346 run->frame_in_tick += frames_left;
347 if(run->frame_in_tick > tick_len) {
348 // We advanced a tick
349 run->tbm_bar_start_tick++;
350 run->tbm_tick++;
351 if(run->tbm_tick > pat->tbm_ticks_per_beat) {
352 // We advanced a beat
353 run->tbm_tick = 1;
354 run->tbm_beat++;
356 if(run->tbm_beat > pat->tbm_beats_per_bar) {
357 // We advanced a bar
358 run->tbm_bar_start_tick = 1;
359 run->tbm_beat = 1;
360 run->tbm_bar++;
362 if(run->tbm_bar*pat->tbm_beats_per_bar > pat->longest_pv->nbeats) {
363 // We advanced to the next pattern
364 pat = ctx->next_pattern_func(ctx);
369 frames_left -= (run->frame_in_tick - tick_len);
370 run->frame_in_tick = 0;
371 } else {
372 // still in same tick
373 frames_left = 0;
376 // End BBT calculations
378 if(pattern_advanced) {
379 run->prev = run->curr;
380 run->curr = ctx->next_pattern_func(ctx);
382 if(run->curr && run->curr->bpm_relation == Absolute) {
383 run->base_beats_per_minute = run->curr->beats_per_minute;
387 return 0;
390 void timebase(jack_transport_state_t state,
391 jack_nframes_t nframes,
392 jack_position_t *pos,
393 int new_pos,
394 void *arg) {
396 pos->valid = (JackPositionBBT | JackBBTFrameOffset);
398 qn_context_t* ctx = (qn_context_t*)arg;
400 if(new_pos && pos->frame==0) {
401 // Set the song time
402 qn_reset_playhead(ctx);
403 } else {
404 // Report the song time
405 pos->frame = ctx->run->elapsed;
408 qn_runstate_t* run = ctx->run;
409 // BBT fields
410 pos->bar = run->tbm_bar;
411 pos->beat = run->tbm_beat;
412 pos->tick = run->tbm_tick;
413 pos->bar_start_tick = run->tbm_bar_start_tick;
414 pos->bbt_offset = run->frame_in_tick;
415 if(run->curr) {
416 pos->beats_per_bar = run->curr->tbm_beats_per_bar;
417 pos->beat_type = run->curr->tbm_beat_type;
418 pos->ticks_per_beat = run->curr->tbm_ticks_per_beat;
419 // TODO: set bpm to interpolated value as per transport.h
420 pos->beats_per_minute = get_effective_bpm(run->curr,run->base_beats_per_minute);