added missing files

This commit is contained in:
Mr.Blinky 2018-09-18 18:19:10 +02:00 committed by GitHub
parent 753aa84d90
commit 71b27ac33c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 967 additions and 0 deletions

View File

@ -0,0 +1,31 @@
BSD 3-Clause License
Copyright (c) 2016 - 2018, TEAM a.r.g.
Copyright (c) 2016 - 2018, Davey Taylor
Copyright (c) 2016 - 2018, Joeri Gantois
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,115 @@
#ATMlib
ATMlib stands for **Arduboy Tracker Music** and is based on [_**Squawk**_](https://github.com/stg/Squawk "Squawk Github Page") a minimalistic 8-bit software synthesizer & playroutine library for Arduino, created by Davey Taylor aka STG.
While _Squawk_ provides a very nice synth, it wasn't optimized for a small footprint. Songs are not very efficient in size, so Joeri Gantois aka JO3RI asked Davey to help him work on a new songformat and so ATMlib was born.
Contributers:
* Davey Taylor - ATMsynth - Effects
* Joeri Gantois - Effects
### FILE/ARRAY FORMAT DESCRIPTION
|**Section** | **Field** | **Type** | **Description**
|--- | --- | ---------------- | ---
|**Track table** | | | **Number of tracks and their addresses**
| | Track count | UBYTE (8-bits) | Number of tracks in the file/array
| | Address track 1 | UWORD (16-bits) | Location in the file/array for track 1
| | … | … | …
| | Address track *__N__* | UWORD (16-bits) | Location in the file/array for track *__N__ (0 … 255)*
| | | |
|**Channel entry tracks** | | | **For each channel, track to start with**
| | Channel 0 track | UBYTE (8-bits) | Starting track index for channel 0
| | … | … | …
| | Channel 3 track | UBYTE (8-bits) | Starting track index for channel 3
| | | |
|**Track 0** | | | **Commands and parameters for track 0**
| | Command 0 | UBYTE (8-bits) | See command list
| | *and its* Parameters | none/variable | *See __parameter list__ for each command*
| | … | … | …
| | Command N | UBYTE (8-bits) |
| | *and its* Parameters | none/variable |
|**…** | **…** | **…** | **…**
|**Track _N_** | | | **Commands and parameters for track _N_** *(0-255)*
### COMMAND LIST
|**Command (_X_)** | **Parameter** | **Type** | **Description**
|--- | --- | ------------------ | ---
|**0<br/>0x00** | | | Stop playing
| | | |
|**1…63<br/>0x00+__X__** | note *(__X__)* | UBYTE (8-bits) | Start playing note *[__X__]* where 1 is a C1.<br/>See [Frequency to Tone](./extras/frequencyToTone.md "Frequency to Tone table")<br/>**_Note:_** everytime a note is played, volume is re-triggered
| | | |
|**64…159<br/>0x40…0x9F** | | | Configure effects (fx)
| | *See __fx list__* | none/variable | Effect is *[__X__ - 64]*
| | | |
|**160…223<br/>0x9F+__t__** | Ticks (*__t__*) | UBYTE (8-bits) | Delay for *[__X__ - 159]* or *[__t__]* ticks<br/>**_Note:_** delay of 0 does not exist and maximum is 64 ticks
| | | |
|**224, __Y__<br/>0xE0, __Y__** | Ticks (*__Y__*) | VLE (8/16-bits) | Long delay for *[__Y__ + 65]* ticks<br/> **_Note:_** LONG delay starts at 1 higher than SHORT delay
|  | | |
|**~~225…251~~** | | | ~~RESERVED~~
|  | | |
|**252, N<br/>0xFC, N** | Track *__N__* | UBYTE (8-bits) | Call/run/goto specified track<br/>Track index where *__N__* is the number of the track to go to
|  | | |
|**253, Y, N<br/>0xFD, Y, N** | | | Repeated call/run/goto specified track
| | Loop count (*__Y__*) | UBYTE (8-bits) | Repeat *[__Y__ + 1]* times (total)
| | Track *__N__* | UBYTE (8-bits) | Track index where *__N__* is the number of the track to go to
|  | | |
|**254<br/>0xFE** | | | Return/end of track marker
| | | |
|**255, L, D<br/>0xFF, L, D** | | | Binary data
| | Length *__L__* | VLE (8/16-bits) | Length in bytes of data to follow
| | Data *__D__* | variable | Binary data chunk (notify host application)
### FX LIST
|**Effect** | **Parameter** | **Type** | **Description**
|--- | ------------------------ | --------------------------- | ---
|**64+0<br/>64<br/>0x40** | set Volume (*__X__*) | UBYTE (8-bit) | Set volume to *[__X__]*.<br/>**_Note:_** If the combined volume of all channels<br/>exceed 255 there may be rollover distortion. This<br/>should not be disallowed, as it may be usesful as|<br/>an effects hack for the musician. There should<br/>however be a non-interfering warning when a<br/>musician enters a value above 63 for ch 1-3 or 32<br/>for ch 4 (noise). ch 4 the volume is counted double,<br/>so 32 is actually 64.
|**64+1<br/>65<br/>0x41** | slide Volume ON (*__X__*) | UBYTE (8-bit) | Slide the volume with an amount (positive or<br/>negative) of *[__X__]* for every tick.<br/>**_Note:_** This results in a fade-in or fade-out<br/>effect. There should be a non-|interfering warning<br/>when sliding would result in exceeding 63 for<br/>ch 1-3 and 32 for ch 4.
|**64+2<br/>66<br/>0x42** | slide Volume ON<br/>advanced (*__X__*) (*__Y__*) | UBYTE (8-bit)<br/>UBYTE (8-bit) | Slide the volume with an amount (positive or<br/>negative) of *[__X__]* for every [*__Y__*] ticks.<br/>*[__Y__]* includes 2 parameters: RRtttttt<br/>R = reserved and t = ticks.
|**64+3<br/>67<br/>0x43** | slide Volume OFF | | Stops the volume slide
|**64+4<br/>68<br/>0x44** | slide Frequency ON (*__X__*) | UBYTE (8-bit) | Slide the frequency with an amount (positive or<br/>negative) of *[__X__]* for every tick. <br/>**_Note:_** The amount of slide is limited<br/>between -127 to 127
|**64+5<br/>69<br/>0x45** | slide Frequency ON<br/>advanced (*__X__*) (*__Y__*) | UBYTE (8-bit)<br/>UBYTE (8-bit) | Slide the frequency with an amount (positive or<br/>negative) of *[__X__]* for every [*__Y__*] ticks.<br/>*[__Y__]* includes 2 parameters: RRtttttt<br/>R = reserved and t = ticks.
|**64+6<br/>70<br/>0x46** | slide Frequency OFF | | Stops the frequency slide
|**64+7<br/>71<br/>0x47** | set Arpeggio (*__X__*)(*__Y__*) | UBYTE (8-bit)<br/>UBYTE (8-bit) | Next to the current playing note, play a second<br/>and third note *[__X__]* for every *[__Y__]* ticks.<br/>*[__X__]* includes 2 parameters: AAAABBBB, where<br/>AAAA = base + amount to |second note and<br/>BBBB = second note + amount to third note.<br/>*[__Y__]* includes 4 parameters: FEDttttt,<br/>where F = reserved, E = toggle no third note,<br/>D = toggle retrigger, ttttt = tick amount.<br/>**_Note:_** Arpeggio is used for playing 3 notes<br/>out of a chord indivually
|**64+8<br/>72<br/>0x48** | Arpeggio OFF | | Stops the arpeggio
|**64+9<br/>73<br/>0x49** | SET Retriggering noise ON (*__X__*) | UBYTE (8-bit) | Noise channel consists of white noise. By setting<br/>retriggering *[__X__]* it swithes the entrypoint at<br/>a given speed. *[__X__]* includes 2 parameters:<br/>AAAAAABB , where AAAAAA |= entry point and<br/>BB = speed (0 = fastest, 1 = faster , 2 = fast)
|**64+10<br/>74<br/>0x4A** | Retriggering noise OFF | | Stops the retriggering for the noise on channel 3
|**64+11<br/>75<br/>0x4B** | add Transposition (*__X__*) | UBYTE (8-bit) | Shifts the played notes by adding *[__X__]* to<br/>the existing transposition for all playing notes.<br/>**_Note:_** The amount of shift is limited<br/>between -127 to 127. However there |should be<br/>a non-interfering warning when transposing would<br/>result in exceeding 63 or get lower than 0
|**64+12<br/>76<br/>0x4C** | set Transposition (*__X__*) | UBYTE (8-bit) | Shifts the played notes by setting the transposition<br/>to [__X__]* for all playing notes.<br/>**_Note:_** The amount of shift is limited<br/>between -127 to 127. However there should |be a<br/>non-interfering warning when transposing would<br/>result in exceeding 63 or get lower than 0
|**64+13<br/>77<br/>0x4D** | Transposition OFF | | Stops the transposition
|**64+14<br/>78<br/>0x4E** | set Tremolo (*__X__*)(*__Y__*) | UBYTE (8-bit)<br/>UBYTE (8-bit) | *[__X__]* sets Depth.<br/>*[__Y__]* includes 4 parameters<br/>RxxBBBBB R = Retrig, x = reserved , B = rate<br/>**_Note:_** Tremolo and Vibrato can **NOT**<br/>be combined in the same |stack
|**64+15<br/>79<br/>0x4F** | Tremolo OFF | | Stops the tremolo
|**64+16<br/>80<br/>0x50** | set Vibrato (*__X__*)(*__Y__*) | UBYTE (8-bit)<br/>UBYTE (8-bit) | *[__X__]* sets Depth.<br/>*[__Y__]* includes 4 parameters<br/>RxxBBBBB R = Retrig, x = reserved , B = rate<br/>**_Note:_** Tremolo and Vibrato can **NOT**<br/>be combined in the same |stack
|**64+17<br/>81<br/>0x51** | Vibrato OFF | | Stops the vibrato
|**64+18<br/>82<br/>0x52** | SET Glissando (*__X__*) | UBYTE (8-bit) | *[__X__]* includes 2 parameters: Vttttttt<br/>V = value ( 0 = go 1 note up, 1 = go 1 note down)<br/>and t = amount of ticks, between each step
|**64+19<br/>83<br/>0x53** | Glissando OFF | | Stops the Glissando
|**64+20<br/>84<br/>0x54** | SET Note Cut (*__X__*) | UBYTE (8-bit) | *[__X__]* sets the equal amount of ticks<br/>between note ON and OFF
|**64+21<br/>85<br/>0x55** | Note Cut OFF | | Stops the Note Cut
|**…** | **…** | **…** | **…**
|**64+92<br/>156<br/>0x9C** | ADD song tempo (*__X__*) | UBYTE (8-bit) | adds *[__X__]* to the tempo of the song.<br/>Total value should be between 0 - 127<br/>**_Note:_** the higher the tempo to more CPU it takes.
|**64+93<br/>157<br/>0x9D** | SET song tempo (*__X__*) | UBYTE (8-bit) | (re-)sets *[__X__]* as the tempo of the song.<br/>Standard is 25. Value should be between 0 - 127<br/>**_Note:_** the higher the tempo to more CPU it takes.
|**64+94<br/>158<br/>0x9E** | GOTO advanced<br/><br/>(*__W__*)<br/>(*__X__*)<br/>(*__Y__*)<br/>(*__Z__*) | <br/><br/>UBYTE (8-bit)<br/>UBYTE (8-bit)<br/>UBYTE (8-bit)<br/>UBYTE (8-bit) | **_Note:_** handy command for having an intro<br/>and a repeating song part<br/>For channel __0__ go to track __W__<br/>For channel __1__ go to track __X__<br/>For channel __2__ go to track __Y__<br/>For channel __3__ go to track __Z__
|**64+95<br/>159<br/>0x9F** | STOP current channel | | channel is no longer being processed<br/>**_Note:_** if all channels have reached STOP, the song ends
|~~TBD~~ | ~~TBD~~ | ~~TBD~~ | ~~TBD~~
#### Thoughts on effects:
**Note:** These are the primitives to be implemented in the playroutine effects processor. Most will have several effect command numbers associated with them for various aspects of the same primitive. Effects can be combined but not stacked, but some combinations may have undesired/interesting interference.
* Volume slide: a gradual increasing or decreasing of the volume.
* Frequency slide: a gradual increasing or decreasing of the [frequency](https://en.wikipedia.org/wiki/Frequency "frequency wikipedia").
* Arpeggio: a group of [notes](https://en.wikipedia.org/wiki/Musical_note "note wikipedia") which are rapidly and automatically played one after the other.
* Retriggering (on [note](https://en.wikipedia.org/wiki/Musical_note "note wikipedia") or by automation): oscillators are restarted either automatically or at the start of each new note.
* Transposition (also for microtonals): play [notes](https://en.wikipedia.org/wiki/Musical_note "note wikipedia") in a different key, or fine tune notes to provide microtonals; frequencies that are in between notes.
* Tremolo: a slight, rapid and regular fluctuation in the amplitude/volume of a [note](https://en.wikipedia.org/wiki/Musical_note "note wikipedia").
* Vibrato: a slight, rapid and regular fluctuation in the [pitch](https://en.wikipedia.org/wiki/Pitch_(music) "pitch wikipedia") of a [note](https://en.wikipedia.org/wiki/Musical_note "note wikipedia").
* Glissando: controls if and how a gradual frequency slide "snaps" to adjacent notes.
* Note cut (with delay and automation): provides a method to stutter and adjust note timing.

View File

@ -0,0 +1,14 @@
{
"name": "ATMlib",
"keywords": "arduboy, game, sound, music",
"description": "A library for playing 4 channel chiptune music and oscilator sounds, intended for use with the Arduboy game system",
"repository":
{
"type": "git",
"url": "https://github.com/TEAMarg/ATMlib.git"
},
"version": "1.2.5",
"exclude": "extras",
"frameworks": "arduino",
"platforms": "atmelavr"
}

View File

@ -0,0 +1,10 @@
name=ATMlib
version=1.2.5
author=TEAM a.r.g., Davey Taylor aka STG, Joeri Gantois aka JO3RI
maintainer=info@TEAM_arg.org
sentence=The Arduboy Tracker Music library.
paragraph=A library for playing 4 channel chiptune music and oscilator sounds, intended for use with the Arduboy game system
category=Other
url=https://github.com/TEAMarg/ATMlib
architectures=avr
includes=ATMlib.h

View File

@ -0,0 +1,428 @@
#include "ATMlib.h"
#ifndef AB_ALTERNATE_WIRING
ATMLIB_CONSTRUCT_ISR(OCR4A)
#else
ATMLIB_CONSTRUCT_ISR(OCR4A,OCR4D)
#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 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) {
cia_count = 1;
// cleanUp stuff first
memset(channel, 0, sizeof(channel));
ChannelActiveMute = 0b11110000;
// 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
TIMSK4 = 0b00000100;
// 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 (unsigned n = 0; n < 4; n++) {
channel[n].ptr = getTrackPointer(pgm_read_byte(song++));
}
}
// 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 << 0 );
}
void ATMsynth::unMuteChannel(byte ch) {
ChannelActiveMute &= (~(1 << 0 ));
}
__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(&noteTable[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(&noteTable[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(&noteTable[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(&noteTable[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
{
memset(channel, 0, sizeof(channel));
TIMSK4 = 0; // Disable interrupt
}
}
}
}

View File

@ -0,0 +1,369 @@
#ifndef _ATMLIB_H_
#define _ATMLIB_H_
#include <stddef.h>
#include <inttypes.h>
#include <Arduino.h>
#define CH_ZERO 0
#define CH_ONE 1
#define CH_TWO 2
#define CH_THREE 3
extern byte trackCount;
extern const word *trackList;
extern const byte *trackBase;
extern uint8_t pcm;
extern bool half;
class ATMsynth {
public:
ATMsynth() {};
// Load and play specified song
void play(const byte *song);
// Play or Pause playback
void playPause();
// Stop playback (unloads song)
void stop();
void muteChannel(byte ch);
void unMuteChannel(byte ch);
};
// oscillator structure
typedef struct {
uint8_t vol;
uint16_t freq;
uint16_t phase;
} osc_t;
typedef osc_t Oscillator;
extern osc_t osc[4];
uint16_t read_vle(const byte **pp);
static inline const byte *getTrackPointer(byte track);
extern void ATM_playroutine() asm("ATM_playroutine");
#ifndef AB_ALTERNATE_WIRING
#define ATMLIB_CONSTRUCT_ISR(TARGET_REGISTER) \
uint16_t __attribute__((used)) cia, __attribute__((used)) cia_count; \
ISR(TIMER4_OVF_vect, ISR_NAKED) { \
asm volatile( \
"push r2 " "\n\t" \
"in r2, __SREG__ " "\n\t" \
"push r18 " "\n\t" \
"lds r18, half \n\t" \
"com r18 \n\t" \
"sts half, r18 \n\t" \
"breq continue \n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
"continue: \n\t" \
"push r27 " "\n\t" \
"push r26 " "\n\t" \
"push r0 " "\n\t" \
"push r1 " "\n\t" \
\
"lds r18, osc+2*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+2*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+2*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+2*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+2*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+2*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"mov r27, r1 " "\n\t" \
"sbrc r27, 7 " "\n\t" \
"com r27 " "\n\t" \
"lsl r27 " "\n\t" \
"lds r26, osc+2*%[mul]+%[vol] " "\n\t" \
"subi r27, 128 " "\n\t" \
"muls r27, r26 " "\n\t" \
"lsl r1 " "\n\t" \
"mov r26, r1 " "\n\t" \
\
"lds r18, osc+0*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+0*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+0*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+0*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+0*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+0*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"mov r18, r1 " "\n\t" \
"lsl r18 " "\n\t" \
"and r18, r1 " "\n\t" \
"lds r27, osc+0*%[mul]+%[vol] " "\n\t" \
"sbrc r18, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"lds r18, osc+1*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+1*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+1*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+1*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+1*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+1*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"lds r27, osc+1*%[mul]+%[vol] " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"ldi r27, 1 " "\n\t" \
"lds r0, osc+3*%[mul]+%[fre] " "\n\t" \
"lds r1, osc+3*%[mul]+%[fre]+1" "\n\t" \
"add r0, r0 " "\n\t" \
"adc r1, r1 " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"eor r0, r27 " "\n\t" \
"sbrc r1, 6 " "\n\t" \
"eor r0, r27 " "\n\t" \
"sts osc+3*%[mul]+%[fre], r0 " "\n\t" \
"sts osc+3*%[mul]+%[fre]+1, r1 " "\n\t" \
\
"lds r27, osc+3*%[mul]+%[vol] " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"lds r27, pcm " "\n\t" \
"add r26, r27 " "\n\t" \
"sts %[reg], r26 " "\n\t" \
\
"lds r27, cia_count+1 " "\n\t" \
"lds r26, cia_count " "\n\t" \
"sbiw r26, 1 " "\n\t" \
"breq call_playroutine " "\n\t" \
"sts cia_count+1, r27 " "\n\t" \
"sts cia_count, r26 " "\n\t" \
"pop r1 " "\n\t" \
"pop r0 " "\n\t" \
"pop r26 " "\n\t" \
"pop r27 " "\n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
"call_playroutine: " "\n\t" \
\
"lds r27, cia+1 " "\n\t" \
"lds r26, cia " "\n\t" \
"sts cia_count+1, r27 " "\n\t" \
"sts cia_count, r26 " "\n\t" \
\
"sei " "\n\t" \
"push r19 " "\n\t" \
"push r20 " "\n\t" \
"push r21 " "\n\t" \
"push r22 " "\n\t" \
"push r23 " "\n\t" \
"push r24 " "\n\t" \
"push r25 " "\n\t" \
"push r30 " "\n\t" \
"push r31 " "\n\t" \
\
"clr r1 " "\n\t" \
"call ATM_playroutine " "\n\t" \
\
"pop r31 " "\n\t" \
"pop r30 " "\n\t" \
"pop r25 " "\n\t" \
"pop r24 " "\n\t" \
"pop r23 " "\n\t" \
"pop r22 " "\n\t" \
"pop r21 " "\n\t" \
"pop r20 " "\n\t" \
"pop r19 " "\n\t" \
\
"pop r1 " "\n\t" \
"pop r0 " "\n\t" \
"pop r26 " "\n\t" \
"pop r27 " "\n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
: \
: [reg] "M" _SFR_MEM_ADDR(TARGET_REGISTER), \
[mul] "M" (sizeof(Oscillator)), \
[pha] "M" (offsetof(Oscillator, phase)), \
[fre] "M" (offsetof(Oscillator, freq)), \
[vol] "M" (offsetof(Oscillator, vol)) \
); \
}
#else
#define ATMLIB_CONSTRUCT_ISR(TARGET_REGISTER,TARGET_REGISTER2) \
uint16_t __attribute__((used)) cia, __attribute__((used)) cia_count; \
ISR(TIMER4_OVF_vect, ISR_NAKED) { \
asm volatile( \
"push r2 " "\n\t" \
"in r2, __SREG__ " "\n\t" \
"push r18 " "\n\t" \
"lds r18, half \n\t" \
"com r18 \n\t" \
"sts half, r18 \n\t" \
"breq continue \n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
"continue: \n\t" \
"push r27 " "\n\t" \
"push r26 " "\n\t" \
"push r0 " "\n\t" \
"push r1 " "\n\t" \
\
"lds r18, osc+2*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+2*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+2*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+2*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+2*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+2*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"mov r27, r1 " "\n\t" \
"sbrc r27, 7 " "\n\t" \
"com r27 " "\n\t" \
"lsl r27 " "\n\t" \
"lds r26, osc+2*%[mul]+%[vol] " "\n\t" \
"subi r27, 128 " "\n\t" \
"muls r27, r26 " "\n\t" \
"lsl r1 " "\n\t" \
"mov r26, r1 " "\n\t" \
\
"lds r18, osc+0*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+0*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+0*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+0*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+0*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+0*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"mov r18, r1 " "\n\t" \
"lsl r18 " "\n\t" \
"and r18, r1 " "\n\t" \
"lds r27, osc+0*%[mul]+%[vol] " "\n\t" \
"sbrc r18, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"lds r18, osc+1*%[mul]+%[fre] " "\n\t" \
"lds r0, osc+1*%[mul]+%[pha] " "\n\t" \
"add r0, r18 " "\n\t" \
"sts osc+1*%[mul]+%[pha], r0 " "\n\t" \
"lds r18, osc+1*%[mul]+%[fre]+1" "\n\t" \
"lds r1, osc+1*%[mul]+%[pha]+1" "\n\t" \
"adc r1, r18 " "\n\t" \
"sts osc+1*%[mul]+%[pha]+1, r1 " "\n\t" \
\
"lds r27, osc+1*%[mul]+%[vol] " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"ldi r27, 1 " "\n\t" \
"lds r0, osc+3*%[mul]+%[fre] " "\n\t" \
"lds r1, osc+3*%[mul]+%[fre]+1" "\n\t" \
"add r0, r0 " "\n\t" \
"adc r1, r1 " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"eor r0, r27 " "\n\t" \
"sbrc r1, 6 " "\n\t" \
"eor r0, r27 " "\n\t" \
"sts osc+3*%[mul]+%[fre], r0 " "\n\t" \
"sts osc+3*%[mul]+%[fre]+1, r1 " "\n\t" \
\
"lds r27, osc+3*%[mul]+%[vol] " "\n\t" \
"sbrc r1, 7 " "\n\t" \
"neg r27 " "\n\t" \
"add r26, r27 " "\n\t" \
\
"lds r27, pcm " "\n\t" \
"add r26, r27 " "\n\t" \
"sts %[reg], r26 " "\n\t" \
"sts %[reg2], r26 " "\n\t" \
\
"lds r27, cia_count+1 " "\n\t" \
"lds r26, cia_count " "\n\t" \
"sbiw r26, 1 " "\n\t" \
"breq call_playroutine " "\n\t" \
"sts cia_count+1, r27 " "\n\t" \
"sts cia_count, r26 " "\n\t" \
"pop r1 " "\n\t" \
"pop r0 " "\n\t" \
"pop r26 " "\n\t" \
"pop r27 " "\n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
"call_playroutine: " "\n\t" \
\
"lds r27, cia+1 " "\n\t" \
"lds r26, cia " "\n\t" \
"sts cia_count+1, r27 " "\n\t" \
"sts cia_count, r26 " "\n\t" \
\
"sei " "\n\t" \
"push r19 " "\n\t" \
"push r20 " "\n\t" \
"push r21 " "\n\t" \
"push r22 " "\n\t" \
"push r23 " "\n\t" \
"push r24 " "\n\t" \
"push r25 " "\n\t" \
"push r30 " "\n\t" \
"push r31 " "\n\t" \
\
"clr r1 " "\n\t" \
"call ATM_playroutine " "\n\t" \
\
"pop r31 " "\n\t" \
"pop r30 " "\n\t" \
"pop r25 " "\n\t" \
"pop r24 " "\n\t" \
"pop r23 " "\n\t" \
"pop r22 " "\n\t" \
"pop r21 " "\n\t" \
"pop r20 " "\n\t" \
"pop r19 " "\n\t" \
\
"pop r1 " "\n\t" \
"pop r0 " "\n\t" \
"pop r26 " "\n\t" \
"pop r27 " "\n\t" \
"pop r18 " "\n\t" \
"out __SREG__, r2 " "\n\t" \
"pop r2 " "\n\t" \
"reti " "\n\t" \
: \
: [reg] "M" _SFR_MEM_ADDR(TARGET_REGISTER), \
[reg2] "M" _SFR_MEM_ADDR(TARGET_REGISTER2), \
[mul] "M" (sizeof(Oscillator)), \
[pha] "M" (offsetof(Oscillator, phase)), \
[fre] "M" (offsetof(Oscillator, freq)), \
[vol] "M" (offsetof(Oscillator, vol)) \
); \
}
#endif
#endif