Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
//
// music.cpp
//
// Created by Peter Barrett on 7/17/14.
// Copyright (c) 2014 Peter Barrett. All rights reserved.
//
#include "config.h"
#include "music.h"
#include "music_tables.h"
#ifndef ARDUINO // Include in video.cpp when building on arduino
// Music has low level and high level kernels
// Low level kernel
// runs every line (15750000/1001 hz)
// mixes channels, steps noise
// emits pcm
// High level kernel
// runs every frame (60 hz)
// steps note envelopes
// traverses music tracks
// Instrument defs
// envelopes
// vol,duration?
// Patches?
#define E_SUSTAIN 0xFF
// Inline envelopes TODO
const uint8_t envelope_NONE[] PROGMEM = {
240,E_SUSTAIN,0 // Attack,decay,sustain, 0 terminated
};
const uint8_t envelope_KEYBOARD[] PROGMEM = {
120,240,160,E_SUSTAIN,60,1,0 // Attack,decay,sustain
};
const uint8_t envelope_HIHAT[] PROGMEM = {
120,240,208,120,96,64,32,1,0
};
const uint8_t envelope_HIT[] PROGMEM = {
120,240,240,120,0
};
const uint8_t envelope_FADE[] PROGMEM = {
120,240,200,160,120,80,40,1,0
};
const uint8_t envelope_FADE2x[] PROGMEM = {
120,240,220,200,180,160,140,120,100,80,60,40,20,1,0
};
const uint8_t envelope_FADE2[] PROGMEM = {
120,240,220,200,180,160,160,160,160,160,160,160,140,120,100,80,60,40,20,1,0
};
const uint8_t* get_envelope(uint8_t i)
{
switch(i) {
case KEYBOARD: return envelope_KEYBOARD;
case HIHAT: return envelope_HIHAT;
case FADE: return envelope_FADE;
case FADE2: return envelope_FADE2;
case HIT: return envelope_HIT;
}
return envelope_NONE;
}
// 6 bytes per
typedef struct {
const uint8_t* envelope; //
uint8_t envelope_pos;
uint8_t velocity;
uint8_t note;
uint8_t slide;
} AudioPatch;
//============================================================================
//============================================================================
// Sample rate is 15750000/1001 or 15734.273hz
// Every second frame? Alternate with IR? todo
AudioChannel _channels[4]; // low level
static AudioPatch _patches[4]; // high level
uint8_t _mix;
typedef struct {
uint8_t delay;
const uint8_t* track;
} music_state;
music_state _music_state[2] = {0};
//static uint8_t music_delay = 0;
//const uint8_t* music_track = 0;
void music_on(uint8_t channel, uint8_t volume, uint16_t step)
{
AudioChannel& c = _channels[channel];
c.volume = volume;
c.step = step;
}
void music_off(uint8_t channel)
{
_channels[channel].volume = 0;
}
void music_volume(uint8_t channel, uint8_t volume)
{
_patches[channel].velocity = volume >> 2; // 0..63
if (volume == 0)
_channels[channel].volume = 0; // pass
}
bool music_playing(uint8_t index)
{
return _music_state[index].track != 0;
}
void music_flag(uint8_t channel, uint8_t flags)
{
}
void music_note(uint8_t channel, uint8_t note, uint8_t velocity)
{
AudioPatch& p = _patches[channel];
p.velocity = velocity; // TODO
p.note = note;
p.slide = 0;
if (!p.envelope)
p.envelope = get_envelope(0);
p.envelope_pos = 0;
}
void music_note_off(uint8_t channel)
{
AudioPatch& p = _patches[channel];
if (p.velocity)
{
if (pgm_read_byte(p.envelope+p.envelope_pos) == E_SUSTAIN)
p.envelope_pos++;
else {
music_off(channel);
p.velocity = 0;
}
}
}
void music_slide(uint8_t channel, int8_t slide)
{
_patches[channel].slide = slide;
}
int8_t music_step_track(uint8_t index)
{
music_state& m = _music_state[index];
if (!m.track || !m.delay || --m.delay) // ready for next step
return 0;
const uint8_t* d = m.track;
for (;;)
{
uint8_t cmd = pgm_read_byte(d++);
switch (cmd & 0xE0) {
case 0:
{
switch (cmd) {
case M_DELAY:
m.delay = pgm_read_byte(d++);
m.track = d;
return 0;
case M_WAVE:
case M_ENVELOPE:
case M_SLIDE:
case M_VOLUME:
{
uint8_t p0 = pgm_read_byte(d++);
uint8_t mask = p0;
p0 >>= 4;
uint8_t m = 1;
for (uint8_t i = 0; i < 4; i++)
{
if (m & mask)
{
switch (cmd)
{
case M_WAVE: _channels[i].flags = p0; break;
case M_ENVELOPE: _patches[i].envelope = get_envelope(p0); break;
case M_SLIDE: music_slide(i,(int8_t)pgm_read_byte(d++)); break;
case M_VOLUME: music_volume(i,pgm_read_byte(d++)); break;
}
}
m <<= 1;
}
}
break;
default:
_music_state[index].track = 0;
return 0;
}
}
break;
case M_DELAY_N:
m.delay = cmd & 0x1F;
m.track = d;
return 0;
case M_NOTES_OFF:
{
uint8_t m = 1;
for (uint8_t i = 0; i < 4; i++)
{
if (m & cmd)
music_note_off(i);
m <<= 1;
}
}
break;
case M_NOTES_ON:
{
uint8_t m = 1;
for (uint8_t i = 0; i < 4; i++)
{
if (m & cmd)
music_note(i,pgm_read_byte(d++));
m <<= 1;
}
}
break;
default: // ERRORz
m.track = d;
return 0;
}
}
}
void music_kernel()
{
// Step music track
music_step_track(0);
music_step_track(1);
// Step patches
for (uint8_t i = 0; i < 4; i++)
{
AudioPatch& p = _patches[i];
if (!p.velocity)
continue;
const uint8_t* ep = p.envelope + p.envelope_pos;
uint8_t e = pgm_read_byte(ep);
uint8_t v = e == E_SUSTAIN ? pgm_read_byte(ep-1) : e;
uint8_t vol = (v*p.velocity) >> 8; // apply volume envelope
uint8_t note = p.note;
p.note += p.slide;
uint16_t step;
if (note >= 128)
step = note-128; // Direct control of low freq stepping
else
step = pgm_read_word(&_midi_tables[note]);
music_on(i,vol,step);
if (e == 0)
p.velocity = 0;
else if (e != E_SUSTAIN)
p.envelope_pos++;
}
}
void music_play(const uint8_t* track, uint8_t index)
{
_music_state[index].track = track;
_music_state[index].delay = 1;
music_step_track(index);
}
//=====================================================================================
//=====================================================================================
// Low level kernel
inline
void audio_step()
{
uint8_t i = 4;
while (i--)
_channels[i].pos += _channels[i].step;
}
inline
void audio_mix()
{
int16_t mix = 0;
uint8_t i;
for (i = 0; i < 4; i++)
{
AudioChannel* ch = _channels+i;
int8_t v = ch->volume;
if (!v)
continue;
int8_t a;
#if ARDUINO
uint8_t flags = ch->flags;
uint16_t* p = (uint16_t*)(&ch->flags-1); // funny addressing hack for speed
a = pgm_read_byte(_wave_tables + *p);
#else
uint8_t hi = ch->pos >> 8; // STEP
a = (int8_t)pgm_read_byte(_wave_tables + hi + (ch->flags << 8));
#endif
mix += a*v;
}
_mix = mix >> 8; // Saturating lookup? filtering? TODO
}
// Simulator
void AUDIO_STEP()
{
audio_step();
audio_mix();
}
#endif
d902f88a4This file is pinned to the selected release. Download the original to open it in your design tool.