793 lines
12 KiB
C++
793 lines
12 KiB
C++
/*
|
|
Epichord - a midi sequencer
|
|
Copyright (C) 2008 Evan Rinehart
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to
|
|
|
|
The Free Software Foundation, Inc.
|
|
51 Franklin Street, Fifth Floor
|
|
Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#ifndef seq_h
|
|
#define seq_h
|
|
|
|
#define MIDI_NOTE_OFF 0x80
|
|
#define MIDI_NOTE_ON 0x90
|
|
#define MIDI_AFTERTOUCH 0xA0
|
|
#define MIDI_CONTROLLER_CHANGE 0xB0
|
|
#define MIDI_PROGRAM_CHANGE 0xC0
|
|
#define MIDI_CHANNEL_PRESSURE 0xD0
|
|
#define MIDI_PITCH_WHEEL 0xE0
|
|
|
|
#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:
|
|
int tick;
|
|
|
|
int type;
|
|
int value1;
|
|
int value2;
|
|
int dur; //for note on events
|
|
|
|
//struct mevent* off; //for note on events
|
|
//int off_index; //used when reloading
|
|
|
|
struct mevent* prev;
|
|
struct mevent* next;
|
|
|
|
int selected;
|
|
int modified;
|
|
|
|
mevent(){
|
|
type = -1;
|
|
//off = NULL;
|
|
prev = NULL;
|
|
next = NULL;
|
|
dur = 32;
|
|
selected = 0;
|
|
modified = 0;
|
|
}
|
|
|
|
mevent(int ztype, int ztick, int zv1){
|
|
//off=NULL;
|
|
prev=NULL;
|
|
next=NULL;
|
|
dur = 32;
|
|
type=ztype;
|
|
tick=ztick;
|
|
value1=zv1;
|
|
value2=0x7f;
|
|
selected = 0;
|
|
modified = 0;
|
|
}
|
|
|
|
mevent(mevent* e){
|
|
type = e->type;
|
|
tick = e->tick;
|
|
value1 = e->value1;
|
|
value2 = e->value2;
|
|
dur = e->dur;
|
|
//off = e->off;
|
|
prev = e->prev;
|
|
next = e->next;
|
|
selected = e->selected;
|
|
modified = e->modified;
|
|
}
|
|
|
|
};
|
|
|
|
struct pattern {
|
|
public:
|
|
struct mevent* events;
|
|
struct pattern* next;
|
|
int ref_c;
|
|
|
|
unsigned char r1,g1,b1;//main color
|
|
unsigned char r2,g2,b2;//bottom color
|
|
unsigned char r3,g3,b3;//top color
|
|
unsigned char rx,gx,bx;//xray color
|
|
|
|
float h, s, v; //color
|
|
|
|
void regen_colors();
|
|
|
|
pattern();
|
|
pattern(pattern* p);
|
|
~pattern();
|
|
void append(mevent* ze);
|
|
void insert(mevent* ze, int tick);
|
|
void fixdur();
|
|
};
|
|
|
|
|
|
|
|
struct layerstack {
|
|
pattern** array;
|
|
|
|
int index;
|
|
int total;
|
|
int memsize;
|
|
|
|
int ref_c;
|
|
|
|
pattern* push_new();
|
|
void push(pattern* p);
|
|
pattern* pop();
|
|
void remove(int n);
|
|
void insert(pattern* p, int n);
|
|
pattern* next();
|
|
pattern* prev();
|
|
void reallocate();
|
|
|
|
layerstack(){};
|
|
layerstack(pattern* p);
|
|
~layerstack();
|
|
};
|
|
|
|
|
|
struct seqpat {
|
|
int track;
|
|
int tick;
|
|
int dur;
|
|
struct pattern* p;
|
|
struct mevent* skip;
|
|
struct seqpat* prev;
|
|
struct seqpat* next;
|
|
|
|
layerstack* layers;
|
|
|
|
// unsigned char color[3][3];
|
|
int selected;
|
|
int modified;
|
|
int record_flag;//0=on record, erase/save. 1=dont
|
|
|
|
int scrollx, scrolly;
|
|
|
|
void restate();
|
|
void apply_erase();
|
|
void apply_layer();
|
|
void next_layer();
|
|
void prev_layer();
|
|
int layer_index();
|
|
int layer_total();
|
|
void record_check(int mode);
|
|
void autocomplete();
|
|
|
|
|
|
seqpat(){
|
|
p = NULL;
|
|
skip = NULL;
|
|
prev = NULL;
|
|
next = NULL;
|
|
dur=0;
|
|
selected=0;
|
|
modified=0;
|
|
record_flag=1;
|
|
layers = NULL;
|
|
scrollx = 0;
|
|
scrolly = 300;
|
|
}
|
|
|
|
seqpat(int ztrack, int ztick, int zdur, pattern* zp){
|
|
p = zp;
|
|
p->ref_c++;
|
|
track = ztrack;
|
|
dur = zdur;
|
|
tick = ztick;
|
|
skip = NULL;
|
|
prev = NULL;
|
|
next = NULL;
|
|
selected = 0;
|
|
modified = 0;
|
|
record_flag = 1;
|
|
layers = NULL;
|
|
scrollx = 0;
|
|
scrolly = 300;
|
|
}
|
|
|
|
seqpat(seqpat* zs){
|
|
p = zs->p;
|
|
p->ref_c++;
|
|
track = zs->track;
|
|
dur = zs->dur;
|
|
tick = zs->tick;
|
|
if(p){p->ref_c++;}
|
|
|
|
skip = p->events;
|
|
prev = zs->prev;
|
|
next = zs->next;
|
|
|
|
scrollx = zs->scrollx;
|
|
scrolly = zs->scrolly;
|
|
|
|
selected = zs->selected;
|
|
modified = zs->modified;
|
|
|
|
record_flag = 1;
|
|
layers = NULL;
|
|
}
|
|
|
|
seqpat(seqpat* zs, pattern* zp){
|
|
p = zp;
|
|
p->ref_c++;
|
|
track = zs->track;
|
|
dur = zs->dur;
|
|
tick = zs->tick;
|
|
if(p){p->ref_c++;}
|
|
|
|
skip = p->events;
|
|
prev = zs->prev;
|
|
next = zs->next;
|
|
|
|
scrollx = zs->scrollx;
|
|
scrolly = zs->scrolly;
|
|
|
|
selected = zs->selected;
|
|
modified = zs->modified;
|
|
|
|
record_flag=1;
|
|
layers = NULL;
|
|
}
|
|
|
|
~seqpat(){
|
|
if(p){
|
|
p->ref_c--;
|
|
if(p->ref_c == 0){
|
|
delete p;
|
|
}
|
|
}
|
|
if(layers){
|
|
layers->ref_c--;
|
|
if(layers->ref_c == 0){
|
|
delete layers;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct track {
|
|
int port;
|
|
int chan;
|
|
int prog;
|
|
int bank;
|
|
int mute;
|
|
int solo;
|
|
int vol;
|
|
int pan;
|
|
char* name;
|
|
int alive;
|
|
seqpat* head;
|
|
seqpat* skip;
|
|
|
|
OnBitArray onbits;
|
|
|
|
int contr[128];
|
|
int pressure;
|
|
int pitchwheel;
|
|
|
|
int modified;
|
|
|
|
void restate();
|
|
|
|
track(){
|
|
port = 0;
|
|
chan = 0;
|
|
prog = 0;
|
|
bank = 0;
|
|
mute = 0;
|
|
solo = 0;
|
|
vol = 127;
|
|
pan = 64;
|
|
name = (char*)malloc(8);
|
|
name[0] = '\0';
|
|
alive = 1;
|
|
head = new seqpat(0,0,0,new pattern());
|
|
head->tick = 0;
|
|
skip = head;
|
|
modified = 0;
|
|
|
|
pressure = 64;
|
|
pitchwheel = 0;
|
|
contr[7] = 64;
|
|
contr[10] = 64;
|
|
contr[32] = 0;
|
|
}
|
|
|
|
~track(){
|
|
free(name);
|
|
seqpat* s = head;
|
|
seqpat* next;
|
|
while(s){
|
|
next = s->next;
|
|
delete s;
|
|
s = next;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
void tswap(T* old, T* nu){
|
|
nu->next = old->next;
|
|
nu->prev = old->prev;
|
|
|
|
if(old->prev){
|
|
old->prev->next = nu; //'atomic'
|
|
}
|
|
if(old->next){
|
|
old->next->prev = nu; //prev ptrs are only read by gui thread
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void tremove(T* old){
|
|
if(old->prev){
|
|
old->prev->next = old->next; //'atomic'
|
|
}
|
|
if(old->next){
|
|
old->next->prev = old->prev; //prev ptrs are only read by gui thread
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void tinsert(T* targ, T* nu){
|
|
nu->prev = targ;
|
|
nu->next = targ->next;
|
|
|
|
targ->next = nu; //'atomic'
|
|
if(nu->next){
|
|
nu->next->prev = nu; //prev ptrs are only read by gui thread
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
T* tfind(T* begin, int tick){
|
|
T* ptr = begin;
|
|
while(ptr->next){
|
|
if(ptr->next->tick > tick){
|
|
break;
|
|
}
|
|
ptr = ptr->next;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
mevent* find_off(mevent* e);
|
|
|
|
|
|
class Command {
|
|
|
|
public:
|
|
|
|
virtual void undo() {}
|
|
virtual void redo() {}
|
|
|
|
};
|
|
|
|
void set_undo(Command* c);
|
|
void push_undo(int n);
|
|
void do_undo();
|
|
void do_redo();
|
|
|
|
int clear_undos(int number);
|
|
|
|
|
|
|
|
class CreateSeqpat : public Command {
|
|
protected:
|
|
seqpat* s;
|
|
|
|
public:
|
|
|
|
CreateSeqpat(){}
|
|
CreateSeqpat(int track, int tick, seqpat* zs, int copy);
|
|
|
|
~CreateSeqpat(){
|
|
if(s->p->ref_c-- == 0){
|
|
delete s->p;
|
|
}
|
|
delete s;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class CreateSeqpatBlank : public CreateSeqpat {
|
|
|
|
public:
|
|
|
|
CreateSeqpatBlank(int track, int tick, int len);
|
|
|
|
};
|
|
|
|
|
|
class DeleteSeqpat : public Command {
|
|
seqpat* s;
|
|
|
|
public:
|
|
|
|
DeleteSeqpat(seqpat* zs){
|
|
s = zs;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class ResizeSeqpat : public Command {
|
|
seqpat* s1;
|
|
seqpat* s2;
|
|
|
|
public:
|
|
|
|
ResizeSeqpat(seqpat* zs, int dur){
|
|
s1 = zs;
|
|
s2 = new seqpat(zs);
|
|
s2->dur = dur;
|
|
}
|
|
~ResizeSeqpat(){
|
|
delete s2;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class MoveSeqpat : public Command {
|
|
seqpat* s;
|
|
seqpat *targ1, *targ2;
|
|
int track1, track2;
|
|
int tick1, tick2;
|
|
|
|
public:
|
|
|
|
MoveSeqpat(seqpat* zs, int track, int tick);
|
|
~MoveSeqpat(){}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
|
|
class SplitSeqpat : public Command {
|
|
seqpat* s;
|
|
seqpat* s1;
|
|
seqpat* s2;
|
|
|
|
public:
|
|
|
|
SplitSeqpat(seqpat* zs, int tick);
|
|
|
|
~SplitSeqpat(){
|
|
//delete s1 and s2
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
|
|
|
|
class JoinSeqpat : public Command {
|
|
seqpat* s1;
|
|
seqpat* s2;
|
|
seqpat* s;
|
|
|
|
public:
|
|
|
|
JoinSeqpat(seqpat* zs1, seqpat* zs2);
|
|
|
|
~JoinSeqpat(){
|
|
//delete s
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class ClearSeqpat : public Command {
|
|
|
|
seqpat* s;
|
|
pattern* p1;
|
|
pattern* p2;
|
|
|
|
public:
|
|
|
|
ClearSeqpat(seqpat* zs){
|
|
s = zs;
|
|
p1 = s->p;
|
|
p2 = new pattern();
|
|
p2->ref_c = 1;
|
|
}
|
|
|
|
~ClearSeqpat(){
|
|
//delete p2
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class LayerSeqpat : public Command {
|
|
|
|
seqpat* s;
|
|
pattern* p;
|
|
|
|
public:
|
|
|
|
LayerSeqpat(seqpat* zs){
|
|
s = zs;
|
|
p = new pattern();
|
|
p->ref_c = 1;
|
|
}
|
|
|
|
~LayerSeqpat(){
|
|
//delete p2
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
|
|
|
|
|
|
class CreateNote : public Command {
|
|
pattern* p;
|
|
mevent* e1;
|
|
mevent* e2;
|
|
|
|
public:
|
|
|
|
CreateNote(pattern* zp, int note, int vel, int tick, int dur){
|
|
p = zp;
|
|
e1 = new mevent(MIDI_NOTE_ON, tick, note);
|
|
e1->dur = dur;
|
|
e1->value2 = vel;
|
|
e2 = new mevent(MIDI_NOTE_OFF, tick+dur, note);
|
|
e2->value2 = 0;
|
|
//e->off = new mevent(MIDI_NOTE_OFF, tick+dur, note);
|
|
}
|
|
~CreateNote(){
|
|
//delete e->off;
|
|
delete e1;
|
|
delete e2;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class CreateNoteOn : public Command {
|
|
pattern* p;
|
|
mevent* e1;
|
|
|
|
public:
|
|
|
|
CreateNoteOn(pattern* zp, int note, int vel, int tick, int dur){
|
|
p = zp;
|
|
e1 = new mevent(MIDI_NOTE_ON, tick, note);
|
|
e1->value2 = vel;
|
|
e1->dur = dur;
|
|
}
|
|
~CreateNoteOn(){
|
|
delete e1;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class CreateNoteOff : public Command {
|
|
pattern* p;
|
|
mevent* e1;
|
|
mevent* e2;
|
|
|
|
int dur1;
|
|
int dur2;
|
|
|
|
public:
|
|
|
|
CreateNoteOff(pattern* zp, int note, int vel, int tick);
|
|
~CreateNoteOff(){
|
|
delete e2;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class DeleteNote : public Command {
|
|
pattern* p;
|
|
mevent* e1;
|
|
mevent* e2;
|
|
|
|
public:
|
|
|
|
DeleteNote(pattern* zp, mevent* ze){
|
|
p = zp;
|
|
e1 = ze;
|
|
e2 = find_off(e1);
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class MoveNote : public Command {
|
|
pattern* p;
|
|
mevent* e1;
|
|
mevent* e2;
|
|
int t1;
|
|
int t2;
|
|
int note1;
|
|
int note2;
|
|
|
|
void arrive(int t);
|
|
|
|
public:
|
|
|
|
MoveNote(pattern* zp, mevent* ze, int zt, int znote){
|
|
p = zp;
|
|
e1 = ze;
|
|
e2 = find_off(e1);
|
|
note1 = ze->value1;
|
|
note2 = znote;
|
|
t1 = ze->tick;
|
|
t2 = zt;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
|
|
class ResizeNote : public Command {
|
|
pattern* p;
|
|
mevent* l1;
|
|
int d1;
|
|
int d2;
|
|
//mevent* l2;
|
|
mevent* r1;
|
|
mevent* r2;
|
|
|
|
public:
|
|
|
|
ResizeNote(pattern* zp, mevent* ze, int dur);
|
|
~ResizeNote(){
|
|
if(r2){
|
|
delete r2;
|
|
}
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
|
|
};
|
|
|
|
|
|
|
|
class CreateEvent : public Command {
|
|
pattern* p;
|
|
mevent* e;
|
|
|
|
public:
|
|
|
|
CreateEvent(pattern* zp, int type, int tick, int value1, int value2){
|
|
p = zp;
|
|
e = new mevent(type,tick,value1);
|
|
e->value2 = value2;
|
|
e->prev = tfind<mevent>(zp->events,tick);
|
|
e->next = e->prev->next;
|
|
}
|
|
~CreateEvent(){
|
|
delete e;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class DeleteEvent : public Command {
|
|
mevent* e;
|
|
|
|
public:
|
|
|
|
DeleteEvent(mevent* ze){
|
|
e = ze;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
class ChangeEvent : public Command {
|
|
mevent* e1;
|
|
mevent* e2;
|
|
|
|
public:
|
|
|
|
ChangeEvent(mevent* ze, int zv1, int zv2){
|
|
e1 = ze;
|
|
e2 = new mevent(ze);
|
|
e2->value1 = zv1;
|
|
e2->value2 = zv2;
|
|
}
|
|
|
|
void redo();
|
|
void undo();
|
|
};
|
|
|
|
|
|
|
|
int play_seq(int cur_tick);
|
|
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);
|
|
void undo_push(int n);
|
|
void do_undo();
|
|
void do_redo();
|
|
void undo_reset();
|
|
|
|
|
|
void reset_record_flags();
|
|
|
|
|
|
//encodes data in e as a midi event placed in buf
|
|
int midi_encode(mevent* e, int chan, unsigned char* buf, size_t* n);
|
|
|
|
//decodes midi data and creates a new mevent
|
|
int midi_decode(char* buf, mevent* e);
|
|
|
|
#endif
|