#include "ATMlib.h" uint16_t __attribute__((used)) cia, __attribute__((used)) cia_count; #ifdef __AVR_ARCH__ ISR(TIMER4_OVF_vect, ISR_NAKED) { asm volatile( "push r18 \n" "lds r18, half \n" // half = !half; "sbrc r18, 0 \n" // if (half) return; "rjmp 2f \n" "ldi r18, 0xFF \n" "1: \n" "sts half, r18 \n" "pop r18 \n" "reti \n" "2: \n" "push r0 \n" "push r1 \n" "push r2 \n" "in r2, __SREG__ \n" "push r30 \n" "push r31 \n" "ldi r30, lo8(osc) \n" "ldi r31, hi8(osc) \n" "ldi r18, 1 \n" "ldd r0, Z+3*%[mul]+%[fre] \n" // uint16_t freq = osc[3].freq; //noise frequency "ldd r1, Z+3*%[mul]+%[fre]+1 \n" "add r0, r0 \n" // freq <<= 1; "adc r1, r1 \n" "sbrc r1, 7 \n" // if (freq & 0x8000) freq ^= 1; "eor r0, r18 \n" "sbrc r1, 6 \n" // if (freq & 0x4000) freq ^= 1; "eor r0, r18 \n" "std Z+3*%[mul]+%[fre], r0 \n" // osc[3].freq = freq; "std Z+3*%[mul]+%[fre]+1, r1 \n" "ldd r18, Z+3*%[mul]+%[vol] \n" // int8_t vol = osc[3].vol; "sbrc r1, 7 \n" // if (freq & 0x8000) vol = -vol; "neg r18 \n" "mov r1, r18 \n" "ldd r0, Z+0*%[mul]+%[fre] \n" // osc[0].phase += osc[0].freq; // update pulse phase "ldd r18, Z+0*%[mul]+%[pha] \n" "add r18, r0 \n" "std Z+0*%[mul]+%[pha], r18 \n" "ldd r0, Z+0*%[mul]+%[fre]+1 \n" "ldd r18, Z+0*%[mul]+%[pha]+1 \n" "adc r18, r0 \n" "std Z+0*%[mul]+%[pha]+1, r18 \n" "ldd r0, Z+0*%[mul]+%[vol] \n" // int8_t vol0 = osc[0].vol; "cpi r18, 192 \n" // if (uint8_t(osc[0].phase >> 8) >= 192) vol0 = -vol0; "brcs 3f \n" "neg r0 \n" "add r1, r0 \n" // int8_t vol += vol0; "3: \n" "ldd r18, Z+1*%[mul]+%[fre] \n" // osc[1].phase += osc[1].freq; // update square phase "ldd r0, Z+1*%[mul]+%[pha] \n" "add r0, r18 \n" "std Z+1*%[mul]+%[pha], r0 \n" "ldd r18, Z+1*%[mul]+%[fre]+1 \n" "ldd r0, Z+1*%[mul]+%[pha]+1 \n" "adc r0, r18 \n" "std Z+1*%[mul]+%[pha]+1, r0 \n" "ldd r18, Z+1*%[mul]+%[vol] \n" // int8_t vol1 = osc[1].vol; "sbrc r0, 7 \n" // if (osc[1].phase & 0x8000) vol1 = -vol1; "neg r18 \n" "add r1, r18 \n" // vol += vol1; "ldd r18, Z+2*%[mul]+%[fre] \n" // osc[2].phase += osc[2].freq;// update triangle phase "ldd r0, Z+2*%[mul]+%[pha] \n" "add r0, r18 \n" "std Z+2*%[mul]+%[pha], r0 \n" "ldd r0, Z+2*%[mul]+%[fre]+1 \n" "ldd r18, Z+2*%[mul]+%[pha]+1 \n" "adc r18, r0 \n" "std Z+2*%[mul]+%[pha]+1, r18 \n" // int8_t phase2 = osc[2].phase >> 8; "ldd r30, Z+2*%[mul]+%[vol] \n" // int8_t vol2 = osc[2].vol; "lds r31, pcm \n" // int8_t tmp = pcm + vol; "add r31, r1 \n" "sbrc r18, 7 \n" // if (phase2 < 0) phase2 = ~phase2; "com r18 \n" "lsl r18 \n" // phase2 <<= 1; "subi r18, 128 \n" // phase2 -= 128; "muls r18, r30 \n" // vol = ((phase2 * vol2) << 1) >> 8 + tmp; "lsl r1 \n" "add r1, r31 \n" "sts %[reg], r1 \n" // reg = vol; #ifdef AB_ALTERNATE_WIRING "sts %[reg2], r1 \n" // reg2 = vol; #endif "lds r31, cia_count+1 \n" // if (--cia_count) return; "lds r30, cia_count \n" "sbiw r30, 1 \n" "brne 4f \n" "lds r31, cia+1 \n" // cia_count = cia; "lds r30, cia \n" "4: \n" "sts cia_count+1, r31 \n" "sts cia_count, r30 \n" "brne 5f \n" "sei \n" // sei(); "push r19 \n" "push r20 \n" "push r21 \n" "push r22 \n" "push r23 \n" "push r24 \n" "push r25 \n" "push r26 \n" "push r27 \n" "clr r1 \n" "call ATM_playroutine \n" // ATM_playroutine(); "pop r27 \n" // } "pop r26 \n" "pop r25 \n" "pop r24 \n" "pop r23 \n" "pop r22 \n" "pop r21 \n" "pop r20 \n" "pop r19 \n" "5: \n" "pop r31 \n" "pop r30 \n" "out __SREG__, r2 \n" "pop r2 \n" "pop r1 \n" "pop r0 \n" "ldi r18, 0x00 \n" "rjmp 1b \n" : : [reg] "M" _SFR_MEM_ADDR(OCR4A), #ifdef AB_ALTERNATE_WIRING [reg2] "M" _SFR_MEM_ADDR(OCR4D), #endif [mul] "M" (sizeof(Oscillator)), [pha] "M" (offsetof(Oscillator, phase)), [fre] "M" (offsetof(Oscillator, freq)), [vol] "M" (offsetof(Oscillator, vol)) ); } #else ISR(TIMER4_OVF_vect) { half = !half; if (half) return; osc[2].phase += osc[2].freq; // update triangle phase int8_t phase2 = osc[2].phase >> 8; if (phase2 < 0) phase2 = ~phase2; phase2 <<= 1; phase2 -= 128; int8_t vol = ((phase2 * int8_t(osc[2].vol)) << 1) >> 8; osc[0].phase += osc[0].freq; // update pulse phase int8_t vol0 = osc[0].vol; if (osc[0].phase >= 0xC000) vol0 = -vol0; vol += vol0; osc[1].phase += osc[1].freq; // update square phase int8_t vol1 = osc[1].vol; if (osc[1].phase & 0x8000) vol1 = -vol1; vol += vol1; uint16_t freq = osc[3].freq; //noise frequency freq <<= 1; if (freq & 0x8000) freq ^= 1; if (freq & 0x4000) freq ^= 1; osc[3].freq = freq; int8_t vol3 = osc[3].vol; if (freq & 0x8000) vol3 = -vol3; vol += vol3; OCR4A = vol + pcm; if (--cia_count) return; cia_count = cia; sei(); ATM_playroutine(); } #endif byte trackCount; byte tickRate; const word *trackList; const byte *trackBase; uint8_t pcm __attribute__((used)) = 128; bool half __attribute__((used)); byte ChannelActiveMute = 0b11110000; // |||||||| // |||||||└-> 0 channel 0 is muted (0 = false / 1 = true) // ||||||└--> 1 channel 1 is muted (0 = false / 1 = true) // |||||└---> 2 channel 2 is muted (0 = false / 1 = true) // ||||└----> 3 channel 3 is muted (0 = false / 1 = true) // |||└-----> 4 channel 0 is Active (0 = false / 1 = true) // ||└------> 5 channel 1 is Active (0 = false / 1 = true) // |└-------> 6 channel 2 is Active (0 = false / 1 = true) // └--------> 7 channel 3 is Active (0 = false / 1 = true) //Imports extern uint16_t cia; // Exports osc_t __attribute__((used)) osc[4]; const word noteTable[64] PROGMEM = { 0, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902, 8372, 8870, 9397, }; struct ch_t { const byte *ptr; byte note; // Nesting word stackPointer[7]; byte stackCounter[7]; byte stackTrack[7]; // note 1 byte stackIndex; byte repeatPoint; // Looping word delay; byte counter; byte track; // External FX word freq; byte vol; // Volume & Frequency slide FX char volFreSlide; byte volFreConfig; byte volFreCount; // Arpeggio or Note Cut FX byte arpNotes; // notes: base, base+[7:4], base+[7:4]+[3:0], if FF => note cut ON byte arpTiming; // [7] = reserved, [6] = not third note ,[5] = retrigger, [4:0] = tick count byte arpCount; // Retrig FX byte reConfig; // [7:2] = , [1:0] = speed // used for the noise channel byte reCount; // also using this as a buffer for volume retrig on all channels // Transposition FX char transConfig; // Tremolo or Vibrato FX byte treviDepth; byte treviConfig; byte treviCount; // Glissando FX char glisConfig; byte glisCount; }; ch_t channel[4]; uint16_t read_vle(const byte **pp) { word q = 0; byte d; do { q <<= 7; d = pgm_read_byte(*pp++); q |= (d & 0x7F); } while (d & 0x80); return q; } static inline const byte *getTrackPointer(byte track) { return trackBase + pgm_read_word(&trackList[track]); } void ATMsynth::play(const byte *song) { stop(); cia_count = 1; // Initializes ATMsynth // Sets sample rate and tick rate tickRate = 25; cia = 15625 / tickRate; // Sets up the ports, and the sample grinding ISR osc[3].freq = 0x0001; // Seed LFSR channel[3].freq = 0x0001; // xFX TCCR4A = 0b01000010; // Fast-PWM 8-bit TCCR4B = 0b00000001; // 62500Hz OCR4C = 0xFF; // Resolution to 8-bit (TOP=0xFF) OCR4A = 0x80; #ifdef AB_ALTERNATE_WIRING TCCR4C = 0b01000101; OCR4D = 0x80; #endif // Load a melody stream and start grinding samples // Read track count trackCount = pgm_read_byte(song++); // Store track list pointer trackList = (word*)song; // Store track pointer trackBase = (song += (trackCount << 1)) + 4; // Fetch starting points for each track for (byte n = 0; n < 4; n++) { channel[n].ptr = getTrackPointer(pgm_read_byte(song++)); } TIMSK4 = 0b00000100;// enable interrupt as last } // Stop playing, unload melody void ATMsynth::stop() { TIMSK4 = 0; // Disable interrupt memset(channel, 0, sizeof(channel)); ChannelActiveMute = 0b11110000; } // Start grinding samples or Pause playback void ATMsynth::playPause() { TIMSK4 = TIMSK4 ^ 0b00000100; // toggle disable/enable interrupt } // Toggle mute on/off on a channel, so it can be used for sound effects // So you have to call it before and after the sound effect void ATMsynth::muteChannel(byte ch) { ChannelActiveMute += (1 << ch ); } void ATMsynth::unMuteChannel(byte ch) { ChannelActiveMute &= (~(1 << ch )); } __attribute__((used)) void ATM_playroutine() { ch_t *ch; // for every channel start working for (byte n = 0; n < 4; n++) { ch = &channel[n]; // Noise retriggering if (ch->reConfig) { if (ch->reCount >= (ch->reConfig & 0x03)) { osc[n].freq = pgm_read_word(¬eTable[ch->reConfig >> 2]); ch->reCount = 0; } else ch->reCount++; } //Apply Glissando if (ch->glisConfig) { if (ch->glisCount >= (ch->glisConfig & 0x7F)) { if (ch->glisConfig & 0x80) ch->note -= 1; else ch->note += 1; if (ch->note < 1) ch->note = 1; else if (ch->note > 63) ch->note = 63; ch->freq = pgm_read_word(¬eTable[ch->note]); ch->glisCount = 0; } else ch->glisCount++; } // Apply volume/frequency slides if (ch->volFreSlide) { if (!ch->volFreCount) { int16_t vf = ((ch->volFreConfig & 0x40) ? ch->freq : ch->vol); vf += (ch->volFreSlide); if (!(ch->volFreConfig & 0x80)) { if (vf < 0) vf = 0; else if (ch->volFreConfig & 0x40) if (vf > 9397) vf = 9397; else if (!(ch->volFreConfig & 0x40)) if (vf > 63) vf = 63; } (ch->volFreConfig & 0x40) ? ch->freq = vf : ch->vol = vf; } if (ch->volFreCount++ >= (ch->volFreConfig & 0x3F)) ch->volFreCount = 0; } // Apply Arpeggio or Note Cut if (ch->arpNotes && ch->note) { if ((ch->arpCount & 0x1F) < (ch->arpTiming & 0x1F)) ch->arpCount++; else { if ((ch->arpCount & 0xE0) == 0x00) ch->arpCount = 0x20; else if ((ch->arpCount & 0xE0) == 0x20 && !(ch->arpTiming & 0x40) && (ch->arpNotes != 0xFF)) ch->arpCount = 0x40; else ch->arpCount = 0x00; byte arpNote = ch->note; if ((ch->arpCount & 0xE0) != 0x00) { if (ch->arpNotes == 0xFF) arpNote = 0; else arpNote += (ch->arpNotes >> 4); } if ((ch->arpCount & 0xE0) == 0x40) arpNote += (ch->arpNotes & 0x0F); ch->freq = pgm_read_word(¬eTable[arpNote + ch->transConfig]); } } // Apply Tremolo or Vibrato if (ch->treviDepth) { int16_t vt = ((ch->treviConfig & 0x40) ? ch->freq : ch->vol); vt = (ch->treviCount & 0x80) ? (vt + ch->treviDepth) : (vt - ch->treviDepth); if (vt < 0) vt = 0; else if (ch->treviConfig & 0x40) if (vt > 9397) vt = 9397; else if (!(ch->treviConfig & 0x40)) if (vt > 63) vt = 63; (ch->treviConfig & 0x40) ? ch->freq = vt : ch->vol = vt; if ((ch->treviCount & 0x1F) < (ch->treviConfig & 0x1F)) ch->treviCount++; else { if (ch->treviCount & 0x80) ch->treviCount = 0; else ch->treviCount = 0x80; } } if (ch->delay) { if (ch->delay != 0xFFFF) ch->delay--; } else { do { byte cmd = pgm_read_byte(ch->ptr++); if (cmd < 64) { // 0 … 63 : NOTE ON/OFF if (ch->note = cmd) ch->note += ch->transConfig; ch->freq = pgm_read_word(¬eTable[ch->note]); if (!ch->volFreConfig) ch->vol = ch->reCount; if (ch->arpTiming & 0x20) ch->arpCount = 0; // ARP retriggering } else if (cmd < 160) { // 64 … 159 : SETUP FX switch (cmd - 64) { case 0: // Set volume ch->vol = pgm_read_byte(ch->ptr++); ch->reCount = ch->vol; break; case 1: case 4: // Slide volume/frequency ON ch->volFreSlide = pgm_read_byte(ch->ptr++); ch->volFreConfig = (cmd - 64) == 1 ? 0x00 : 0x40; break; case 2: case 5: // Slide volume/frequency ON advanced ch->volFreSlide = pgm_read_byte(ch->ptr++); ch->volFreConfig = pgm_read_byte(ch->ptr++); break; case 3: case 6: // Slide volume/frequency OFF (same as 0x01 0x00) ch->volFreSlide = 0; break; case 7: // Set Arpeggio ch->arpNotes = pgm_read_byte(ch->ptr++); // 0x40 + 0x03 ch->arpTiming = pgm_read_byte(ch->ptr++); // 0x40 (no third note) + 0x20 (toggle retrigger) + amount break; case 8: // Arpeggio OFF ch->arpNotes = 0; break; case 9: // Set Retriggering (noise) ch->reConfig = pgm_read_byte(ch->ptr++); // RETRIG: point = 1 (*4), speed = 0 (0 = fastest, 1 = faster , 2 = fast) break; case 10: // Retriggering (noise) OFF ch->reConfig = 0; break; case 11: // ADD Transposition ch->transConfig += (char)pgm_read_byte(ch->ptr++); break; case 12: // SET Transposition ch->transConfig = pgm_read_byte(ch->ptr++); break; case 13: // Transposition OFF ch->transConfig = 0; break; case 14: case 16: // SET Tremolo/Vibrato ch->treviDepth = pgm_read_word(ch->ptr++); ch->treviConfig = pgm_read_word(ch->ptr++) + ((cmd - 64) == 14 ? 0x00 : 0x40); break; case 15: case 17: // Tremolo/Vibrato OFF ch->treviDepth = 0; break; case 18: // Glissando ch->glisConfig = pgm_read_byte(ch->ptr++); break; case 19: // Glissando OFF ch->glisConfig = 0; break; case 20: // SET Note Cut ch->arpNotes = 0xFF; // 0xFF use Note Cut ch->arpTiming = pgm_read_byte(ch->ptr++); // tick amount break; case 21: // Note Cut OFF ch->arpNotes = 0; break; case 92: // ADD tempo tickRate += pgm_read_byte(ch->ptr++); cia = 15625 / tickRate; break; case 93: // SET tempo tickRate = pgm_read_byte(ch->ptr++); cia = 15625 / tickRate; break; case 94: // Goto advanced for (byte i = 0; i < 4; i++) channel[i].repeatPoint = pgm_read_byte(ch->ptr++); break; case 95: // Stop channel ChannelActiveMute = ChannelActiveMute ^ (1 << (n + 4)); ch->vol = 0; ch->delay = 0xFFFF; break; } } else if (cmd < 224) { // 160 … 223 : DELAY ch->delay = cmd - 159; } else if (cmd == 224) { // 224: LONG DELAY ch->delay = read_vle(&ch->ptr) + 65; } else if (cmd < 252) { // 225 … 251 : RESERVED } else if (cmd == 252 || cmd == 253) { // 252 (253) : CALL (REPEATEDLY) byte new_counter = cmd == 252 ? 0 : pgm_read_byte(ch->ptr++); byte new_track = pgm_read_byte(ch->ptr++); if (new_track != ch->track) { // Stack PUSH ch->stackCounter[ch->stackIndex] = ch->counter; ch->stackTrack[ch->stackIndex] = ch->track; // note 1 ch->stackPointer[ch->stackIndex] = ch->ptr - trackBase; ch->stackIndex++; ch->track = new_track; } ch->counter = new_counter; ch->ptr = getTrackPointer(ch->track); } else if (cmd == 254) { // 254 : RETURN if (ch->counter > 0 || ch->stackIndex == 0) { // Repeat track if (ch->counter) ch->counter--; ch->ptr = getTrackPointer(ch->track); //asm volatile (" jmp 0"); // reboot } else { // Check stack depth if (ch->stackIndex == 0) { // Stop the channel ch->delay = 0xFFFF; } else { // Stack POP ch->stackIndex--; ch->ptr = ch->stackPointer[ch->stackIndex] + trackBase; ch->counter = ch->stackCounter[ch->stackIndex]; ch->track = ch->stackTrack[ch->stackIndex]; // note 1 } } } else if (cmd == 255) { // 255 : EMBEDDED DATA ch->ptr += read_vle(&ch->ptr); } } while (ch->delay == 0); if (ch->delay != 0xFFFF) ch->delay--; } if (!(ChannelActiveMute & (1 << n))) { if (n == 3) { // Half volume, no frequency for noise channel osc[n].vol = ch->vol >> 1; } else { osc[n].freq = ch->freq; osc[n].vol = ch->vol; } } // if all channels are inactive, stop playing or check for repeat if (!(ChannelActiveMute & 0xF0)) { byte repeatSong = 0; for (byte j = 0; j < 4; j++) repeatSong += channel[j].repeatPoint; if (repeatSong) { for (byte k = 0; k < 4; k++) { channel[k].ptr = getTrackPointer(channel[k].repeatPoint); channel[k].delay = 0; } ChannelActiveMute = 0b11110000; } else { ATMsynth::stop(); } } } }