mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 23:13:01 +00:00
parent
6f3b9f2e5d
commit
45ce940d66
7 changed files with 92 additions and 18 deletions
|
@ -2537,6 +2537,45 @@ void DivEngine::noteOff(int chan) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
||||||
|
//if (ch<0 || ch>=chans) return;
|
||||||
|
if (midiBaseChan<0) midiBaseChan=0;
|
||||||
|
if (midiBaseChan>=chans) midiBaseChan=chans-1;
|
||||||
|
int finalChan=midiBaseChan;
|
||||||
|
|
||||||
|
if (!playing) {
|
||||||
|
reset();
|
||||||
|
freelance=true;
|
||||||
|
playing=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) {
|
||||||
|
chan[finalChan].midiNote=note;
|
||||||
|
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++finalChan>=chans) {
|
||||||
|
finalChan=0;
|
||||||
|
}
|
||||||
|
} while (finalChan!=midiBaseChan);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivEngine::autoNoteOff(int ch, int note, int vol) {
|
||||||
|
if (!playing) {
|
||||||
|
reset();
|
||||||
|
freelance=true;
|
||||||
|
playing=true;
|
||||||
|
}
|
||||||
|
//if (ch<0 || ch>=chans) return;
|
||||||
|
for (int i=0; i<chans; i++) {
|
||||||
|
if (chan[i].midiNote==note) {
|
||||||
|
pendingNotes.push(DivNoteEvent(i,-1,-1,-1,false));
|
||||||
|
chan[i].midiNote=-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DivEngine::setOrder(unsigned char order) {
|
void DivEngine::setOrder(unsigned char order) {
|
||||||
BUSY_BEGIN_SOFT;
|
BUSY_BEGIN_SOFT;
|
||||||
curOrder=order;
|
curOrder=order;
|
||||||
|
@ -2621,6 +2660,11 @@ bool DivEngine::switchMaster() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivEngine::setMidiBaseChan(int chan) {
|
||||||
|
if (chan<0 || chan>=chans) chan=0;
|
||||||
|
midiBaseChan=chan;
|
||||||
|
}
|
||||||
|
|
||||||
void DivEngine::setMidiCallback(std::function<int(const TAMidiMessage&)> what) {
|
void DivEngine::setMidiCallback(std::function<int(const TAMidiMessage&)> what) {
|
||||||
midiCallback=what;
|
midiCallback=what;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ struct DivChannelState {
|
||||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp;
|
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp;
|
||||||
|
|
||||||
|
int midiNote;
|
||||||
|
|
||||||
DivChannelState():
|
DivChannelState():
|
||||||
note(-1),
|
note(-1),
|
||||||
oldNote(-1),
|
oldNote(-1),
|
||||||
|
@ -126,7 +128,8 @@ struct DivChannelState {
|
||||||
scheduledSlideReset(false),
|
scheduledSlideReset(false),
|
||||||
shorthandPorta(false),
|
shorthandPorta(false),
|
||||||
noteOnInhibit(false),
|
noteOnInhibit(false),
|
||||||
resetArp(false) {}
|
resetArp(false),
|
||||||
|
midiNote(-1) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DivNoteEvent {
|
struct DivNoteEvent {
|
||||||
|
@ -231,6 +234,7 @@ class DivEngine {
|
||||||
short vibTable[64];
|
short vibTable[64];
|
||||||
int reversePitchTable[4096];
|
int reversePitchTable[4096];
|
||||||
int pitchTable[4096];
|
int pitchTable[4096];
|
||||||
|
int midiBaseChan;
|
||||||
|
|
||||||
blip_buffer_t* samp_bb;
|
blip_buffer_t* samp_bb;
|
||||||
size_t samp_bbInLen;
|
size_t samp_bbInLen;
|
||||||
|
@ -539,6 +543,9 @@ class DivEngine {
|
||||||
// stop note
|
// stop note
|
||||||
void noteOff(int chan);
|
void noteOff(int chan);
|
||||||
|
|
||||||
|
void autoNoteOn(int chan, int ins, int note, int vol=-1);
|
||||||
|
void autoNoteOff(int chan, int note, int vol=-1);
|
||||||
|
|
||||||
// go to order
|
// go to order
|
||||||
void setOrder(unsigned char order);
|
void setOrder(unsigned char order);
|
||||||
|
|
||||||
|
@ -638,6 +645,9 @@ class DivEngine {
|
||||||
// switch master
|
// switch master
|
||||||
bool switchMaster();
|
bool switchMaster();
|
||||||
|
|
||||||
|
// set MIDI base channel
|
||||||
|
void setMidiBaseChan(int chan);
|
||||||
|
|
||||||
// set MIDI input callback
|
// set MIDI input callback
|
||||||
// if the specified function returns -2, note feedback will be inhibited.
|
// if the specified function returns -2, note feedback will be inhibited.
|
||||||
void setMidiCallback(std::function<int(const TAMidiMessage&)> what);
|
void setMidiCallback(std::function<int(const TAMidiMessage&)> what);
|
||||||
|
@ -726,6 +736,7 @@ class DivEngine {
|
||||||
view(DIV_STATUS_NOTHING),
|
view(DIV_STATUS_NOTHING),
|
||||||
haltOn(DIV_HALT_NONE),
|
haltOn(DIV_HALT_NONE),
|
||||||
audioEngine(DIV_AUDIO_NULL),
|
audioEngine(DIV_AUDIO_NULL),
|
||||||
|
midiBaseChan(0),
|
||||||
samp_bbInLen(0),
|
samp_bbInLen(0),
|
||||||
samp_temp(0),
|
samp_temp(0),
|
||||||
samp_prevSample(0),
|
samp_prevSample(0),
|
||||||
|
|
|
@ -430,7 +430,7 @@ struct DivInstrument {
|
||||||
DivInstrument():
|
DivInstrument():
|
||||||
name(""),
|
name(""),
|
||||||
mode(false),
|
mode(false),
|
||||||
type(DIV_INS_STD) {
|
type(DIV_INS_FM) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1552,7 +1552,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
switch (msg.type&0xf0) {
|
switch (msg.type&0xf0) {
|
||||||
case TA_MIDI_NOTE_OFF: {
|
case TA_MIDI_NOTE_OFF: {
|
||||||
if (chan<0 || chan>=chans) break;
|
if (chan<0 || chan>=chans) break;
|
||||||
pendingNotes.push(DivNoteEvent(msg.type&15,-1,-1,-1,false));
|
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
|
||||||
if (!playing) {
|
if (!playing) {
|
||||||
reset();
|
reset();
|
||||||
freelance=true;
|
freelance=true;
|
||||||
|
@ -1563,14 +1563,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
||||||
case TA_MIDI_NOTE_ON: {
|
case TA_MIDI_NOTE_ON: {
|
||||||
if (chan<0 || chan>=chans) break;
|
if (chan<0 || chan>=chans) break;
|
||||||
if (msg.data[1]==0) {
|
if (msg.data[1]==0) {
|
||||||
pendingNotes.push(DivNoteEvent(msg.type&15,-1,-1,-1,false));
|
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
|
||||||
} else {
|
} else {
|
||||||
pendingNotes.push(DivNoteEvent(msg.type&15,ins,(int)msg.data[0]-12,msg.data[1],true));
|
autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]);
|
||||||
}
|
|
||||||
if (!playing) {
|
|
||||||
reset();
|
|
||||||
freelance=true;
|
|
||||||
playing=true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) {
|
||||||
selEnd.xFine=xFine;
|
selEnd.xFine=xFine;
|
||||||
selEnd.y=y;
|
selEnd.y=y;
|
||||||
selecting=true;
|
selecting=true;
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) {
|
void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) {
|
||||||
|
@ -88,6 +89,8 @@ void FurnaceGUI::finishSelection() {
|
||||||
if (e->song.chanCollapse[selEnd.xCoarse]) {
|
if (e->song.chanCollapse[selEnd.xCoarse]) {
|
||||||
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
|
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
|
@ -191,6 +194,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||||
}
|
}
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
||||||
|
@ -210,6 +214,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
||||||
cursor.xCoarse=firstChannel;
|
cursor.xCoarse=firstChannel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
|
|
||||||
selStart=cursor;
|
selStart=cursor;
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
|
@ -233,6 +238,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
||||||
cursor.xCoarse=lastChannel-1;
|
cursor.xCoarse=lastChannel-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
|
|
||||||
selStart=cursor;
|
selStart=cursor;
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
|
@ -254,6 +260,7 @@ void FurnaceGUI::moveCursorTop(bool select) {
|
||||||
if (!select) {
|
if (!select) {
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
}
|
}
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +280,7 @@ void FurnaceGUI::moveCursorBottom(bool select) {
|
||||||
selStart=cursor;
|
selStart=cursor;
|
||||||
}
|
}
|
||||||
selEnd=cursor;
|
selEnd=cursor;
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
updateScroll(cursor.y);
|
updateScroll(cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -703,7 +703,15 @@ void FurnaceGUI::stop() {
|
||||||
activeNotes.clear();
|
activeNotes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::previewNote(int refChan, int note) {
|
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
||||||
|
if (autoNote) {
|
||||||
|
e->setMidiBaseChan(refChan);
|
||||||
|
e->synchronized([this,note]() {
|
||||||
|
e->autoNoteOn(-1,curIns,note);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool chanBusy[DIV_MAX_CHANS];
|
bool chanBusy[DIV_MAX_CHANS];
|
||||||
memset(chanBusy,0,DIV_MAX_CHANS*sizeof(bool));
|
memset(chanBusy,0,DIV_MAX_CHANS*sizeof(bool));
|
||||||
for (ActiveNote& i: activeNotes) {
|
for (ActiveNote& i: activeNotes) {
|
||||||
|
@ -725,8 +733,8 @@ void FurnaceGUI::previewNote(int refChan, int note) {
|
||||||
//printf("FAILED TO FIND CHANNEL!\n");
|
//printf("FAILED TO FIND CHANNEL!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) {
|
void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
|
||||||
if (activeNotes.empty()) return;
|
if (activeNotes.empty() && !autoNote) return;
|
||||||
try {
|
try {
|
||||||
int key=noteKeys.at(scancode);
|
int key=noteKeys.at(scancode);
|
||||||
int num=12*curOctave+key;
|
int num=12*curOctave+key;
|
||||||
|
@ -737,6 +745,13 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) {
|
||||||
if (key==101) return;
|
if (key==101) return;
|
||||||
if (key==102) return;
|
if (key==102) return;
|
||||||
|
|
||||||
|
if (autoNote) {
|
||||||
|
e->synchronized([this,num]() {
|
||||||
|
e->autoNoteOff(-1,num);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i=0; i<activeNotes.size(); i++) {
|
for (size_t i=0; i<activeNotes.size(); i++) {
|
||||||
if (activeNotes[i].note==num) {
|
if (activeNotes[i].note==num) {
|
||||||
e->noteOff(activeNotes[i].chan);
|
e->noteOff(activeNotes[i].chan);
|
||||||
|
@ -1008,7 +1023,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
int key=noteKeys.at(ev.key.keysym.scancode);
|
int key=noteKeys.at(ev.key.keysym.scancode);
|
||||||
int num=12*curOctave+key;
|
int num=12*curOctave+key;
|
||||||
if (key!=100 && key!=101 && key!=102) {
|
if (key!=100 && key!=101 && key!=102) {
|
||||||
previewNote(cursor.xCoarse,num);
|
previewNote(cursor.xCoarse,num,true);
|
||||||
}
|
}
|
||||||
} catch (std::out_of_range& e) {
|
} catch (std::out_of_range& e) {
|
||||||
}
|
}
|
||||||
|
@ -1052,7 +1067,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FurnaceGUI::keyUp(SDL_Event& ev) {
|
void FurnaceGUI::keyUp(SDL_Event& ev) {
|
||||||
stopPreviewNote(ev.key.keysym.scancode);
|
stopPreviewNote(ev.key.keysym.scancode,curWindow!=GUI_WINDOW_PATTERN);
|
||||||
if (wavePreviewOn) {
|
if (wavePreviewOn) {
|
||||||
if (ev.key.keysym.scancode==wavePreviewKey) {
|
if (ev.key.keysym.scancode==wavePreviewKey) {
|
||||||
wavePreviewOn=false;
|
wavePreviewOn=false;
|
||||||
|
@ -1923,7 +1938,7 @@ bool FurnaceGUI::loop() {
|
||||||
case TA_MIDI_PROGRAM:
|
case TA_MIDI_PROGRAM:
|
||||||
if (midiMap.programChange) {
|
if (midiMap.programChange) {
|
||||||
curIns=msg.data[0];
|
curIns=msg.data[0];
|
||||||
if (curIns>(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
|
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2693,6 +2708,7 @@ bool FurnaceGUI::init() {
|
||||||
midiLock.lock();
|
midiLock.lock();
|
||||||
midiQueue.push(msg);
|
midiQueue.push(msg);
|
||||||
midiLock.unlock();
|
midiLock.unlock();
|
||||||
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
if (midiMap.at(msg)) return -2;
|
if (midiMap.at(msg)) return -2;
|
||||||
return curIns;
|
return curIns;
|
||||||
});
|
});
|
||||||
|
|
|
@ -988,8 +988,8 @@ class FurnaceGUI {
|
||||||
void play(int row=0);
|
void play(int row=0);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void previewNote(int refChan, int note);
|
void previewNote(int refChan, int note, bool autoNote=false);
|
||||||
void stopPreviewNote(SDL_Scancode scancode);
|
void stopPreviewNote(SDL_Scancode scancode, bool autoNote=false);
|
||||||
|
|
||||||
void keyDown(SDL_Event& ev);
|
void keyDown(SDL_Event& ev);
|
||||||
void keyUp(SDL_Event& ev);
|
void keyUp(SDL_Event& ev);
|
||||||
|
|
Loading…
Reference in a new issue