// find a note in the array of MAX_NOTES available notes to use byte findNoteIndex() { for(byte i=0;i<MAX_NOTES;i++) { // find a slot for this note if (note[i].midiVal <= NOTE_PENDING_OFF) { while ((note[i].midiVal != NOTE_OFF) && ((note[i].phaseInc > 0) || (note[i].phaseFractionInc > 0))) { // wait until note reaches NOTE_OFF state to avoid click } return i; } } // There are no notes that are not being used. Check to see if any // are in RELEASE envelope phase and use it. Find the oldest of the // notes in RELEASE phase. byte noteIndex = 255; unsigned long oldest = millis(); for(byte i=0;i<MAX_NOTES;i++) { if ((note[i].envelopePhase == RELEASE) && (!note[i].isSample) && (note[i].startTime < oldest)) { noteIndex = i; oldest = note[i].startTime; } } if (noteIndex != 255) { stopNote(noteIndex); return noteIndex; } // All notes are being used. Choose the note that is "oldest". // Start by looking for non-sample notes. noteIndex = 255; for(byte i=0;i<MAX_NOTES;i++) { if (!note[i].isSample) { noteIndex = i; break; } } if (noteIndex != 255) { for(byte i=0;i<MAX_NOTES;i++) { if ((!note[i].isSample) && (note[i].startTime < note[noteIndex].startTime)) { // try to find an older nonsample note. noteIndex = i; } } } else { // All notes are samples. Now try to find the oldest. noteIndex = 0; for(byte i=1;i<MAX_NOTES;i++) { if (note[i].startTime < note[noteIndex].startTime) { noteIndex = i; } } } // At this point noteIndex is the oldest note, giving priority to samples. if (note[noteIndex].phaseInc > 0) { stopNote(noteIndex); } return noteIndex; }
void ArduboyTunes::initChannel(byte pin) { byte timer_num; // we are all out of timers if (_tune_num_chans == AVAILABLE_TIMERS) return; timer_num = pgm_read_byte(tune_pin_to_timer_PGM + _tune_num_chans); _tune_pins[_tune_num_chans] = pin; _tune_num_chans++; pinMode(pin, OUTPUT); switch (timer_num) { case 1: // 16 bit timer TCCR1A = 0; TCCR1B = 0; bitWrite(TCCR1B, WGM12, 1); bitWrite(TCCR1B, CS10, 1); _tunes_timer1_pin_port = portOutputRegister(digitalPinToPort(pin)); _tunes_timer1_pin_mask = digitalPinToBitMask(pin); break; case 3: // 16 bit timer TCCR3A = 0; TCCR3B = 0; bitWrite(TCCR3B, WGM32, 1); bitWrite(TCCR3B, CS30, 1); _tunes_timer3_pin_port = portOutputRegister(digitalPinToPort(pin)); _tunes_timer3_pin_mask = digitalPinToBitMask(pin); playNote(0, 60); /* start and stop channel 0 (timer 3) on middle C so wait/delay works */ stopNote(0); break; } }
/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit big-endian number of msec to wait */ void ArduboyTunes::step() { byte command, opcode, chan; unsigned duration; while (1) { command = pgm_read_byte(score_cursor++); opcode = command & 0xf0; chan = command & 0x0f; if (opcode == TUNE_OP_STOPNOTE) { /* stop note */ stopNote(chan); } else if (opcode == TUNE_OP_PLAYNOTE) { /* play note */ playNote(chan, pgm_read_byte(score_cursor++)); } else if (opcode == TUNE_OP_RESTART) { /* restart score */ score_cursor = score_start; } else if (opcode == TUNE_OP_STOP) { /* stop score */ tune_playing = false; break; } else if (opcode < 0x80) { /* wait count in msec. */ duration = ((unsigned)command << 8) | (pgm_read_byte(score_cursor++)); wait_toggle_count = ((unsigned long) wait_timer_frequency2 * duration + 500) / 1000; if (wait_toggle_count == 0) wait_toggle_count = 1; break; } } }
void stopSequencer() { // if any notes are on stop them for(byte t=0;t<SEQ_NUM_TRACKS;t++) { sequenceNote_t *sn = &seq[track[t].noteSeqIndex][t]; if ((note[sn->noteIndex].envelopePhase != OFF)) { stopNote(sn->noteIndex); note[sn->noteIndex].trigger = UNSET; } } if (metronomeNoteIndex != UNSET) { stopNote(metronomeNoteIndex); metronomeNoteIndex = UNSET; } digitalWrite(led[seqLEDIndex], LOW); ledState[seqLEDIndex] = LOW; ledState[PREVIEW_LED] = LOW; seqPreview = false; }
void readGrooveboxButtons() { if (buttonPressedNew(BUTTON1) && (!buttonPressed(BUTTON4))) { // start/stop live sequencer groovebox if (!seqRunning) { resetGroovebox(); seqSynchStart = true; while ((buttonHeld(BUTTON1)) && (seqSynchStart)) { // wait until the button is released or until a MIDI event triggers the start if (buttonPressed(BUTTON4)) { // save procedure return; } #ifdef MIDI_ENABLE MIDI.read(); #endif #ifdef USBMIDI_ENABLE USBMIDI.read(); #endif } } seqSynchStart = false; seqRunning = !seqRunning; if (!seqRunning) { stopSequencer(); } } if (buttonPressedNew(BUTTON2)) { // clear the sequence notes on this track. for(byte i=0;i<SEQ_LENGTH;i++) { seq[i][currentTrack].midiVal = UNSET; seq[i][currentTrack].transpose = 0; } } if ((buttonPressedNew(BUTTON3)) && (seqRunning)) { seqPreview = !seqPreview; } if (seqPreview) { ledState[PREVIEW_LED] = HIGH; } if (buttonPressedNew(BUTTON4)) { metronomeMode = (metronomeMode + 1) % 3; if ((metronomeMode == METRONOME_MODE_OFF) && (metronomeNoteIndex != UNSET)) { stopNote(metronomeNoteIndex); metronomeNoteIndex = UNSET; } } }
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 SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) { if (const SamplerSound* const playingSound = static_cast <SamplerSound*> (getCurrentlyPlayingSound().get())) { const float* const inL = playingSound->data->getSampleData (0, 0); const float* const inR = playingSound->data->getNumChannels() > 1 ? playingSound->data->getSampleData (1, 0) : nullptr; float* outL = outputBuffer.getSampleData (0, startSample); float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getSampleData (1, startSample) : nullptr; while (--numSamples >= 0) { const int pos = (int) sourceSamplePosition; const float alpha = (float) (sourceSamplePosition - pos); const float invAlpha = 1.0f - alpha; // just using a very simple linear interpolation here.. float l = (inL [pos] * invAlpha + inL [pos + 1] * alpha); float r = (inR != nullptr) ? (inR [pos] * invAlpha + inR [pos + 1] * alpha) : l; l *= lgain; r *= rgain; if (isInAttack) { l *= attackReleaseLevel; r *= attackReleaseLevel; attackReleaseLevel += attackDelta; if (attackReleaseLevel >= 1.0f) { attackReleaseLevel = 1.0f; isInAttack = false; } } else if (isInRelease) { l *= attackReleaseLevel; r *= attackReleaseLevel; attackReleaseLevel += releaseDelta; if (attackReleaseLevel <= 0.0f) { stopNote (false); break; } } if (outR != nullptr) { *outL++ += l; *outR++ += r; } else { *outL++ += (l + r) * 0.5f; } sourceSamplePosition += pitchRatio; if (sourceSamplePosition > playingSound->length) { stopNote (false); break; } } } }
void ArduboyTunes::stopScore (void) { for (uint8_t i = 0; i < _tune_num_chans; i++) stopNote(i); tune_playing = false; }
OSStatus AKSampler_Plugin::HandleNoteOff(UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) { //printf("note off: ch%d nn%d vel%d\n", inChannel, inNoteNumber, inVelocity); stopNote(inNoteNumber, false); return noErr; }
int main(void) { // Setup pins DDRB = 0x00; // All pins inputs DDRC = 0x07; // PC0,1,2 as outputs for the LEDs DDRD = 0x0b; // PD0 as output for LED, PD1 for TXD, PD3 for buzzer PORTD = 0xe0; // Pullups for switches PORTB = 0x3f; // Pullups for switches // LEDs LEDOff(); // ADC ADMUX = 0x4f; // start off with mux on internal input ADCSRA = 0x80; // ADC EN DIDR0 = 0x38; // disable digital buffers on ADC pins // Timer TCCR0A = 0x00; // Normal mode TCCR0B = 0x03; // prescaler at 64 results in 125kHz ticks // UART cli(); // disable global interrupts stickInit(); // initialise stick input spektrumInit(); // Get startup mode trainerPlugged = TRAINERPIN; bindSwitch = BINDPIN; transmitMode = 0; if(trainerPlugged == 0) transmitMode = eTM_trainer_master; // fixme or trainer slave, if PPM signal present if(bindSwitch == 0) transmitMode = eTM_bind; // Timer loop! static uint8_t fastScaler = 255; static uint8_t slowScaler = 255; static uint16_t secondScaler = 0xffff; // Buzzer TCCR2B = 0x03; // prescaler at 64 stopNote(); noteBuffer[0] = NOTE6G; noteBuffer[1] = NOTE6GS; noteBuffer[2] = NOTESTOP; noteCounter = 0; noteInterruptable = 0; while(1) { while(TCNT0 < FASTLOOPCOUNT) { if(TCNT0 < FASTLOOPCOUNT && TCNT0 >= FASTLOOPCOUNT-LED0Duty/3) LED0On(); // red LED too bright, reduce duty if(TCNT0 < FASTLOOPCOUNT && TCNT0 >= FASTLOOPCOUNT-LED1Duty) LED1On(); if(TCNT0 < FASTLOOPCOUNT && TCNT0 >= FASTLOOPCOUNT-LED2Duty) LED2On(); if(TCNT0 < FASTLOOPCOUNT && TCNT0 >= FASTLOOPCOUNT-LED3Duty) LED3On(); } TCNT0 = 0; // reduce jitter by resetting soon (SPEKTRUM does not like jitter!) LEDOff(); // should be going at about 625Hz getDigital(); stickGetRawADC(rawstickvalues); if(++fastScaler >= OVERSAMPLE) //should be 22ms for DSMX (Nano Board can not handle it faster!!) { fastScaler = 0; // this loop runs slower than 50Hz fastLoop(); /*throVoltage = 0; ruddVoltage = 0; elevVoltage = 0; aileVoltage = 0;*/ rawstickvalues[0]=0; rawstickvalues[1]=0; rawstickvalues[2]=0; rawstickvalues[3]=0; if(++slowScaler >= 6) { // should be going at about 10Hz slowScaler = 0; slowLoop(); } } // we are here every 2ms if(++secondScaler >=500) { // every second secondScaler=0; IdleSeconds++; if(auxSwitch && mixToggle) { FlySeconds++; if(FlySeconds == FLYSECONDS_MAX) oneTone(NOTE7B); if(FlySeconds == FLYSECONDS_MAX+10) twoTone(NOTE7C); if(IdleSeconds >= FLYSECONDS_MAX*2) { twoTone(NOTE7C); } } } } }
void slowLoop(void) { // Read battery voltage ADMUX = 0x46; // select chan 6 ADCSRA |= 0x40; // start while(ADCSRA & 0x40); // wait for completion uint8_t level = ADC >> 2; // note: level is now approx 26.1x the actual voltage // Battery level pulse speeds if(level > 150) battPulse = 1; // slow pulse else if(level > 137) battPulse = 2; else if(level > 120) battPulse = 4; else { battPulse = 24; // very fast pulses if(noteBuffer[noteCounter] == 0) { noteBuffer[0] = NOTEPAUSE; noteBuffer[1] = NOTEPAUSE; noteBuffer[2] = NOTEPAUSE; noteBuffer[3] = NOTEPAUSE; noteBuffer[4] = NOTEPAUSE; noteBuffer[5] = NOTEPAUSE; noteBuffer[6] = NOTEPAUSE; noteBuffer[7] = NOTEPAUSE; noteBuffer[8] = NOTEPAUSE; noteBuffer[9] = NOTE5C; noteBuffer[10] = NOTE5C; noteBuffer[11] = NOTE5CS; noteBuffer[12] = NOTE5CS; noteBuffer[13] = NOTESTOP; noteCounter = 0; noteInterruptable = 1; } } // Check trainer plug plug static uint8_t trainerPluggedPrev; trainerPlugged = TRAINERPIN; if(trainerPlugged != trainerPluggedPrev) { trainerPluggedPrev = trainerPlugged; if(trainerPlugged) { // Not Plugged IN if(noteInterruptable) { noteBuffer[0] = NOTE6C; noteBuffer[1] = NOTE7C; noteBuffer[2] = NOTE6C; noteBuffer[3] = NOTESTOP; noteCounter = 0; } if(transmitMode == 2 || transmitMode == 3) transmitMode = 0; // escape out of trainer mode } else { // Plugged IN noteBuffer[0] = NOTE6C; // these notes interrupt the startup tune, this is by design noteBuffer[1] = NOTE6C; noteBuffer[2] = NOTE7C; noteBuffer[3] = NOTESTOP; noteCounter = 0; noteInterruptable = 0; if(transmitMode == 0) transmitMode = 3; // enter trainer mode } } // Beeps while toggling or binding beeps if(toggleCounter >= 4 || transmitMode == 1) { if(noteBuffer[noteCounter] == 0) { noteBuffer[0] = NOTEPAUSE; noteBuffer[1] = NOTEPAUSE; noteBuffer[2] = NOTEPAUSE; noteBuffer[3] = NOTEPAUSE; noteBuffer[4] = NOTEPAUSE; noteBuffer[5] = NOTEPAUSE; noteBuffer[6] = NOTEPAUSE; noteBuffer[7] = NOTEPAUSE; noteBuffer[8] = NOTEPAUSE; noteBuffer[9] = NOTE5C; noteBuffer[10] = NOTESTOP; noteCounter = 0; noteInterruptable = 1; } } // Play sounds if(noteBuffer[noteCounter] > 0) { if(noteBuffer[noteCounter] == NOTEPAUSE) { stopNote(); } else { playNote(noteBuffer[noteCounter]); } noteCounter++; } else { stopNote(); noteInterruptable = 1; } }