1
Fork 0

More robust automatic note-off abilities.

Pressing pause, stop, and editing patterns while transport is running
causes playing notes to stop.  Instead of using 'all notes off' controller
change messages, the sequencer keeps track of playing notes and does
a fast search to see which ones to cancel. This reduces midi bus traffic
considerably.

There is a potential race condition because both the audio thread (while
sequencer is playing) and the GUI (when changes are made) manipulate the
bit field used to track notes that are on. A possible error is when the
user presses a control or edits a pattern just as a note should be ending.
By the powers of multithreading, two note offs may be generated when there
should only be one. I am considering this rare and harmless enough at this
time to ignore it. Doing it correctly would require a better serialized,
lock-free command queue, and would solve a few other race conditions. Maybe
some other time.
This commit is contained in:
EvanR 2009-05-25 18:43:03 -05:00
parent 30673a375d
commit 383ca5d308
9 changed files with 153 additions and 26 deletions

View File

@ -70,6 +70,7 @@ static int loop_start = 0;
static int loop_end = TICKS_PER_BEAT*4;
static int passthru = 1;
static int rec_port = 0;
static int trackinit = 1;
static int init_chans = 1;
@ -223,6 +224,7 @@ static int process(jack_nframes_t nframes, void* arg){
for(int i=0; i<n; i++){
md1[i]=buf[i];
}
}
@ -549,17 +551,14 @@ void midi_track_off(int track){
int chan = tracks[track]->chan;
int port = tracks[track]->port;
midi_channel_off(chan,port);
}
void midi_channel_off(int chan, int port){
char buf[3] = {0xB0,123,0};
buf[0] = 0xB0 | chan;
send_midi(buf,3,port);
buf[0] = 0xB0 | chan;
buf[1] = 120;
send_midi(buf,3,port);
OnBitArray* onbits = &(tracks[track]->onbits);
onbits->search_init();
int note = onbits->search_next();
while(note >= 0){
midi_note_off(note, chan, port);
note = onbits->search_next();
}
onbits->clear();
}

View File

@ -27,6 +27,7 @@
#include <fltk/Button.h>
#include <fltk/Input.h>
#include <fltk/ValueInput.h>
#include <fltk/TextDisplay.h>
#include "ui.h"

View File

@ -339,6 +339,7 @@ int PianoRoll::handle(int event){
move_flag = 0;
midi_track_off(cur_seqpat->track);
ui->keyboard->release_note(last_note,0);
ui->keyboard->release_note(move_norig+move_noffset,0);
ui->keyboard->redraw();
@ -352,7 +353,9 @@ int PianoRoll::handle(int event){
if(delete_flag && over_n){
if(over_n->selected){
apply_delete();
midi_track_off(cur_seqpat->track);
ui->event_edit->redraw();
}
}

View File

@ -25,9 +25,9 @@
#include <list>
#include <vector>
#include <fltk/TextDisplay.h>
#include "seq.h"
#include <fltk/TextDisplay.h>
#include "util.h"
#include "backend.h"
@ -106,7 +106,7 @@ int play_seq(int cur_tick){
p = s->p;
e = s->skip;
if(!e){ goto switchpatseq; }//no more events in this seqpat
if(!e){ goto switchblock; }//no more events in this seqpat
again:
@ -122,17 +122,21 @@ again:
e = e->next;
s->skip = e;
if(!e){//no more events in this seqpat
goto switchpatseq;
goto switchblock;
}
}
else if(e->tick > s->dur){//went past the end
goto switchpatseq;
goto switchblock;
}
else{//no more events on this track right now
continue;
}
goto again;//try to play next event
switchpatseq:
switchblock:
//TODO auto off all playing notes on track because end of block
s = s->next;
tracks[i]->skip = s;
@ -151,7 +155,24 @@ switchpatseq:
std::list<total_event>::iterator i = dispatch_queue.begin();
while(i != dispatch_queue.end()){
OnBitArray* oba = &(tracks[i->t]->onbits);
if(i->e->type == MIDI_NOTE_ON){
if(!oba->get(i->e->value1)){
tracks[i->t]->onbits.set(i->e->value1, 1);
}
else{
mevent eoff = i->e;
eoff.type = MIDI_NOTE_OFF;
eoff.value2 = 0;
dispatch_event(&eoff, i->t, i->s->tick);
}
}
else if(i->e->type == MIDI_NOTE_OFF){
tracks[i->t]->onbits.set(i->e->value1, 0);
}
dispatch_event(i->e, i->t, i->s->tick);
i++;
}
@ -817,8 +838,6 @@ void pattern::fixdur(){
}
}
//used when the sequence is changed in such a way
//that the sequencer state needs to be updated
void track::restate(){
@ -957,7 +976,7 @@ void seqpat::autocomplete(){
int chan = tracks[track]->chan;
int port = tracks[track]->port;
printf("use of deprecated function seqpat::autocomplete\n");
while(e){
if(e->type == MIDI_NOTE_ON && e->tick < pos){
eoff = find_off(e);
@ -1164,3 +1183,68 @@ void undo_reset(){
undo_nptr = undo_number.begin();
undo_ptr = undo_stack.begin();
}
OnBitArray::OnBitArray(){
for(int i=0; i<16; i++){
bytes[i] = 0;
}
spos = 0;
jpos = 0;
}
int OnBitArray::get(int note){
//note/8 = note >> 3
//note%8 = note & 0x7
return bytes[note >> 3] & (1 << (note & 0x7));
}
void OnBitArray::set(int note, int value){
//note/8 = note >> 3
//note%8 = note & 0x3
if(value){
bytes[note >> 3] |= (1 << (note & 0x7));
}
else{
bytes[note >> 3] &= ~(1 << (note & 0x7));
}
}
void OnBitArray::search_init(){
spos = 0;
jpos = 0;
}
int OnBitArray::search_next(){
unsigned char byte;
for(int i=spos; i<16; i++){
byte = bytes[i];
if(byte){
for(int j=jpos; j<8; j++){
if(byte & (1<<j)){
if(j==7){
spos = i+1;
jpos = 0;
}
else{
spos = i;
jpos = j+1;
}
int note = i*8 + j;
//bytes[i] &= ~(1<<j);
return note;
}
}
jpos = 0;
}
}
return -1;
}
void OnBitArray::clear(){
for(int i=0; i<16; i++){
bytes[i] = 0;
}
}

View File

@ -33,6 +33,25 @@
#include <stdlib.h>
#include <stdio.h>
class OnBitArray {
unsigned char bytes[16];
int spos;
int jpos;
public:
int get(int note);
void set(int note, int value);
void search_init();
int search_next();
void clear();
OnBitArray();
};
struct mevent {
public:
@ -279,6 +298,8 @@ struct track {
seqpat* head;
seqpat* skip;
OnBitArray onbits;
int modified;
void restate();
@ -738,6 +759,8 @@ int set_seq_pos(int new_tick);
void set_rec_track(int t);
int get_rec_track();
void tracks_auto_off();
int set_default_hsv_value(float v);
void set_undo(Command* c);

View File

@ -46,18 +46,21 @@ void portcb(fltk::Widget* w, long i){
//fltk::ValueInput* o = (fltk::ValueInput*)w;
Gauge* o = (Gauge*)w;
track* t = tracks[i];
int old_port = t->port;
t->port = (int)o->value;
midi_channel_off(t->chan,old_port);
if(t->port != (int)o->value){
midi_track_off(i);
t->port = (int)o->value;
}
}
void chancb(fltk::Widget* w, long i){
//fltk::ValueInput* o = (fltk::ValueInput*)w;
Gauge* o = (Gauge*)w;
track* t = tracks[i];
int old_chan = t->chan;
t->chan = (int)o->value;
midi_channel_off(old_chan,t->port);
if(t->chan != (int)o->value){
midi_track_off(i);
t->chan = (int)o->value;
}
}
void progcb(fltk::Widget* w, long i){

View File

@ -481,7 +481,9 @@ void press_play(){
}
else{
pause_backend();
all_notes_off();
ui->play_button->label("@>");
}
}
@ -494,9 +496,14 @@ void press_stop(){
}
pause_backend();
reset_backend(left);
all_notes_off();
reset_backend(left);
ui->song_timeline->update(left);
ui->pattern_timeline->update(left);

View File

@ -159,3 +159,5 @@ float randf(float l, float r){
return ans;
}

View File

@ -35,4 +35,9 @@ void unmodify_blocks();
int unmodify_and_unstick_tracks();
float randf(float l, float r);
#endif