void readSynthButtons() { readFnButton(); int diff; for(byte i=0;i<N_NOTE_BUTTONS;i++) { if (buttonPressedNew(i)) { if (!fnEnabled) { button[i].pitchReading = sampledAnalogRead(PITCH_POT); } button[i].noteIndex = doNoteOn(selectedSettings+1, button[i].midiVal, MAX_VELOCITY); note[button[i].noteIndex].trigger = i; } else { byte noteIndex = button[i].noteIndex; if ((noteIndex != UNSET) && (note[noteIndex].midiVal > NOTE_PENDING_OFF) && (note[noteIndex].trigger == i)) { if (buttonHeld(i)) { if (!fnEnabled) { int currentPitchReading = sampledAnalogRead(PITCH_POT); diff = abs(currentPitchReading - button[i].pitchReading); if (diff > 5) { // the pot has moved since the button was pressed button[i].midiVal = map(currentPitchReading, 0, 1024, POT_MIDI_LOW, POT_MIDI_HIGH+1); button[i].pitchReading = currentPitchReading; if (button[i].midiVal < MIDI_LOW) { button[i].midiVal = MIDI_LOW; } if (button[i].midiVal > MIDI_HIGH) { button[i].midiVal = MIDI_HIGH; } button[i].pitchReading = currentPitchReading; note[noteIndex].midiVal = button[i].midiVal; } } } else { if (note[noteIndex].envelopePhase != RELEASE) { // button released note[noteIndex].trigger = UNSET; button[i].noteIndex = UNSET; doNoteOff(selectedSettings+1, button[i].midiVal); } } } } } }
void grooveboxNoteOn(byte channelNum, byte midiNote, byte velocity) { seqLock = true; // snap to nearest sequencer position byte seqSnapIndex; int noteStart = pulseClock; // noteStartOffset is the timing error. It is the difference between when the note // was pressed and the nearest sequencer note boundary. int noteStartOffset = (pulseClock % seqStartPulse); boolean snappedForward = false; if ((noteStartOffset > 0) && (noteStartOffset <= (seqStartPulse/2))) { // snap backward seqSnapIndex = seqIndex; } else { // snap forward seqSnapIndex = (seqIndex + 1) % SEQ_LENGTH; snappedForward = true; } if (seqSynchStart) { seqSnapIndex = 0; } sequenceNote_t *sn; note_t *n; byte currentNoteSeqIndex = track[currentTrack].noteSeqIndex; sn = &seq[currentNoteSeqIndex][currentTrack]; n = ¬e[sn->noteIndex]; if (n->trigger == currentTrack) { if ((n->envelopePhase != OFF) || (n->isSample)) { // A note on the current track is playing. if ((!seqPreview) && (seqRunning) && (n->envelopePhase != RELEASE)) { // If we are not previewing and sequence is running, and it's // not in RELEASE phase yet, adjust its duration and stop the note. if ((sn->duration == 0) && (currentNoteSeqIndex != seqSnapIndex)) { // If the note being played has not been released yet, set a duration for it. unsigned int newDuration; if ((snappedForward) && (noteStartOffset > 0)) { newDuration = (noteStart + (seqStartPulse-noteStartOffset)) - sn->startPulse; } else { newDuration = (noteStart - noteStartOffset) - sn->startPulse; } debugprint("setting duration of note ", currentNoteSeqIndex); debugprintln(" to ", newDuration); sn->duration = newDuration; } } stopNote(sn->noteIndex); note[sn->noteIndex].trigger = UNSET; } } byte ni = doNoteOn(selectedSettings+1, midiNote, velocity); if (channelNum == 10) { setDrumParameters(ni, midiNote, velocity); } note[ni].volumeScale = track[currentTrack].volumeScale; if (note[ni].volumeScale != 1.0) { if (note[ni].isSample) { note[ni].doScale = true; } } note[ni].isPreview = seqPreview; if ((!seqPreview) && (seqRunning || seqSynchStart)) { sn = &seq[seqSnapIndex][currentTrack]; sn->noteIndex = ni; track[currentTrack].noteSeqIndex = seqSnapIndex; note[sn->noteIndex].trigger = currentTrack; sn->startPulse = noteStart; sn->midiVal = midiNote; if (channelNum == 10) { sn->midiVal = sn->midiVal | 0x80; // set high bit to indicate ch10 } sn->transpose = 0; track[currentTrack].state = SEQ_RECORD; sn->velocity = velocity; sn->duration = 0; sn->waveform = note[ni].waveform; } if (seqSynchStart) seqSynchStart = false; seqLock = false; }
void doGroovebox() { if (!seqRunning) return; unsigned int duration; sequenceNote_t *sn; for(byte t=0;t<SEQ_NUM_TRACKS;t++) { sn = &seq[track[t].noteSeqIndex][t]; note_t *n = ¬e[sn->noteIndex]; if (track[t].state == SEQ_PLAY) { // check to see if this note's duration is elapsed, but only // if it has not been released. if ((n->trigger == t) && (n->envelopePhase != RELEASE) && (n->envelopePhase != OFF)) { duration = pulseClock - sn->startPulse; if (duration >= sn->duration) { // time to release this note if (duration != sn->duration) { debugprintln("track=", t); debugprintln("noteSeqIndex=", track[t].noteSeqIndex); debugprint("ERROR: releasing note with duration ", sn->duration); debugprintln(" at computed duration ", duration); debugprintln("env=", n->envelopePhase); sn->duration = duration; } releaseNote(n); } } } } if ((pulseClock % seqStartPulse) == 0) { sequenceNote_t *sn; sequenceNote_t *nextsn; seqIndex = (seqIndex + 1) % SEQ_LENGTH; if (seqIndex < 10) { debugprint(" ", seqIndex); } else { debugprint("", seqIndex); } debugprint(":"); seqLEDIndex = seqIndex / (SEQ_LENGTH / 4); digitalWrite(led[seqLEDIndex], HIGH); ledState[seqLEDIndex] = HIGH; // The play/record head has reached a note boundary. for(byte t=0;t<SEQ_NUM_TRACKS;t++) { sn = &seq[track[t].noteSeqIndex][t]; note_t *n = ¬e[sn->noteIndex]; if (track[t].state == SEQ_PLAY) { // Check if it is time to start a new note at this position in the sequence. nextsn = &seq[seqIndex][t]; if (nextsn->midiVal != UNSET) { // There is a note here in the sequence to play // See if there is a currently playing note and stop it. if (n->trigger == t) { if ((n->envelopePhase != RELEASE) && (n->envelopePhase != OFF)) { // A currently playing note should have been released above // because its duration should have elapsed. debugprint(" adjusting note ", track[t].noteSeqIndex); debugprint(" duration from ", sn->duration); debugprintln(" to ", pulseClock - sn->startPulse); sn->duration = pulseClock - sn->startPulse; } // Stop the note stopNote(sn->noteIndex); n->trigger = UNSET; } if ((pulseClock > nextsn->startPulse) && ((pulseClock - nextsn->startPulse) < seqStartPulse)) { // avoid double hit for short note that was already released but snapped to this position debugprintln("avoiding double hit: ", pulseClock-nextsn->startPulse); } else { byte midiVal = (nextsn->midiVal & 0x7F); boolean isDrumChannel = (nextsn->midiVal) & 0x80; // high bit indicates ch10 if (nextsn->waveform < N_WAVEFORMS) { // transpose notes that are not samples midiVal = constrain(nextsn->midiVal + nextsn->transpose, MIDI_LOW, MIDI_HIGH); } nextsn->noteIndex = doNoteOn(t+1, midiVal, nextsn->velocity); if (isDrumChannel) { setDrumParameters(nextsn->noteIndex, midiVal, nextsn->velocity); } note[nextsn->noteIndex].trigger = t; nextsn->startPulse = pulseClock; track[t].noteSeqIndex = seqIndex; // Set the waveform to the one that was recorded. // This only works if we don't call setPhaseIncrement() in doNoteOn() if (!isDrumChannel) { note[nextsn->noteIndex].waveformBuf = waveformBuffers[nextsn->waveform]; } if (nextsn->waveform >= N_WAVEFORMS) { note[nextsn->noteIndex].isSample = true; if (!isDrumChannel) { // already taken care of in setDrumParameters(). note[nextsn->noteIndex].sampleLength = sampleLength[nextsn->waveform - N_WAVEFORMS]; note[nextsn->noteIndex].volIndex = note[nextsn->noteIndex].targetVolIndex; note[nextsn->noteIndex].volume = logVolume[note[nextsn->noteIndex].volIndex]; note[nextsn->noteIndex].volumeNext = note[nextsn->noteIndex].volume; } } else { note[nextsn->noteIndex].isSample = false; } note[nextsn->noteIndex].volumeScale = track[t].volumeScale; if (note[nextsn->noteIndex].volumeScale != 1.0) { if (note[nextsn->noteIndex].isSample) { note[nextsn->noteIndex].doScale = true; } } } } } // track in SEQ_PLAY state if (track[t].state == SEQ_RECORD) { // a note is currently being held and recorded // check to see if there is a note here in the sequence // and if so, overwrite it. if ((seq[seqIndex][t].midiVal != UNSET) && (seqIndex != track[t].noteSeqIndex)) { // Only clear the note if it's not the one being recorded. // I may be the one being recorded because the recording started before // the pulseClock reached this point in the sequence and the new note // "snapped" to this location. seq[seqIndex][t].midiVal = UNSET; seq[seqIndex][t].transpose = 0; // Note that this may not actually be the right thing to do because // the currently recorded note might be released right after the play // head passes this point in the sequence, and the release snaps back to this // point. If that were the case, we'd want to keep the note that is already // here. } } if (seq[seqIndex][t].midiVal != UNSET) { debugprint(" ", (seq[seqIndex][t].midiVal & 0x7F)); if (seq[seqIndex][t].duration > 0) { debugprint("[", seq[seqIndex][t].duration); } else { debugprint("[UNKNOWN"); } debugprint("]"); } else { debugprint(" "); } if (t == (SEQ_NUM_TRACKS-1)) { debugprintln(""); } } // for each track if (metronomeMode != METRONOME_MODE_OFF) { byte tickFrequency; if (metronomeMode == METRONOME_MODE_QUARTER) { tickFrequency = 4; } if (metronomeMode == METRONOME_MODE_SIXTEENTH) { tickFrequency = 1; } if ((seqIndex % tickFrequency) == 0) { if (seqIndex == 0) { metronomeNoteIndex = metronomeTick(64, MAX_NOTE_VOL); } else { metronomeNoteIndex = metronomeTick(60, 40); } } } } if (((pulseClock - (seqStartPulse/2)) % seqStartPulse) == 0) { digitalWrite(led[seqLEDIndex], LOW); ledState[seqLEDIndex] = LOW; } }
void* MidiInput::run() { unsigned char tmp; unsigned char cmd = 0; unsigned char channel = 0; unsigned char data[2]; int inCommand = 0; char ctlName[ 10 ]; //format is "ctl00-000" while( running ) { read( midiFd, &tmp, 1 ); if ( tmp == 248 ) // throw away timing messages they mess us up! { //debug( DEBUG_STATUS, "." ); continue; } if ( tmp < 128 ) { data[0] = tmp; inCommand = 1; } else { cmd = tmp & 0xF0; channel = tmp & 0x0F; inCommand = 0; } switch( cmd ) { case MIDI_NOTEOFF: //0x80 READ2; doNoteOff( channel, data[0], data[1] ); break; case MIDI_NOTEON: //0x90 READ2; if ( data[1] == 0 ) { doNoteOff( channel, data[0], data[1] ); } else { doNoteOn( channel, data[0], data[1] ); } break; case MIDI_KEY_PRESSURE: //0xA0 READ2; debug( DEBUG_STATUS, "KEY PRESSURE ch=%d note=%d amount=%d\n", channel, cmd, data[ 0 ] ); break; case MIDI_CTL_CHANGE: //0xB0 READ2; debug( DEBUG_STATUS, "CTL_CHANGE %d %d %d\n", channel, data[ 0 ], data[ 1 ] ); sprintf( ctlName, "ctl%d-%d", channel, data[0] ); { Output* out; out = MoogObject::getOutput( ctlName ); if ( out != NULL ) out->setData(data[ 1 ] / 127.0); } break; case MIDI_PGM_CHANGE: //0xC0 READ1; debug( DEBUG_STATUS, "PGM_CHANGE %d %d\n", channel, data[ 0 ] ); break; case MIDI_CHN_PRESSURE: //0xD0 READ1; debug( DEBUG_STATUS, "CHN_PRESSURE %d %d\n", channel, data[ 0 ] ); break; case MIDI_PITCH_BEND: //0xE0 READ2; doPitchBend( (( data[ 1 ] << 7 ) & 0xFF00) | data[ 0 ] ); break; case MIDI_SYSTEM_PREFIX: //0xF0 switch( channel ) { case 0: break; } break; default: debug( DEBUG_STATUS, "[%d]\n", cmd ); } } pthread_exit(0); /* not reached */ return( NULL ); }