More work on GU synth

pull/537/head
ZodiusInfuser 2022-08-23 13:59:16 +01:00 zatwierdzone przez Phil Howard
rodzic deec835692
commit 882f76dcbc
10 zmienionych plików z 366 dodań i 318 usunięć

Wyświetl plik

@ -6,7 +6,7 @@ pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pi
target_sources(galactic_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.cpp
${CMAKE_CURRENT_LIST_DIR}/synth.cpp
${CMAKE_CURRENT_LIST_DIR}/../pico_synth/pico_synth.cpp
)
target_include_directories(galactic_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})

Wyświetl plik

@ -45,42 +45,6 @@ static uint16_t b_gamma_lut[256] = {0};
static uint32_t dma_channel;
static uint32_t audio_dma_channel;
static const int16_t sine_waveform[256] = {-32768,-32758,-32729,-32679,-32610,-32522,-32413,-32286,-32138,-31972,-31786,-31581,-31357,-31114,-30853,-30572,-30274,-29957,-29622,-29269,-28899,-28511,-28106,-27684,-27246,-26791,-26320,-25833,-25330,-24812,-24279,-23732,-23170,-22595,-22006,-21403,-20788,-20160,-19520,-18868,-18205,-17531,-16846,-16151,-15447,-14733,-14010,-13279,-12540,-11793,-11039,-10279,-9512,-8740,-7962,-7180,-6393,-5602,-4808,-4011,-3212,-2411,-1608,-804,0,804,1608,2411,3212,4011,4808,5602,6393,7180,7962,8740,9512,10279,11039,11793,12540,13279,14010,14733,15447,16151,16846,17531,18205,18868,19520,20160,20788,21403,22006,22595,23170,23732,24279,24812,25330,25833,26320,26791,27246,27684,28106,28511,28899,29269,29622,29957,30274,30572,30853,31114,31357,31581,31786,31972,32138,32286,32413,32522,32610,32679,32729,32758,32767,32758,32729,32679,32610,32522,32413,32286,32138,31972,31786,31581,31357,31114,30853,30572,30274,29957,29622,29269,28899,28511,28106,27684,27246,26791,26320,25833,25330,24812,24279,23732,23170,22595,22006,21403,20788,20160,19520,18868,18205,17531,16846,16151,15447,14733,14010,13279,12540,11793,11039,10279,9512,8740,7962,7180,6393,5602,4808,4011,3212,2411,1608,804,0,-804,-1608,-2411,-3212,-4011,-4808,-5602,-6393,-7180,-7962,-8740,-9512,-10279,-11039,-11793,-12540,-13279,-14010,-14733,-15447,-16151,-16846,-17531,-18205,-18868,-19520,-20160,-20788,-21403,-22006,-22595,-23170,-23732,-24279,-24812,-25330,-25833,-26320,-26791,-27246,-27684,-28106,-28511,-28899,-29269,-29622,-29957,-30274,-30572,-30853,-31114,-31357,-31581,-31786,-31972,-32138,-32286,-32413,-32522,-32610,-32679,-32729,-32758};
#define SONG_LENGTH 384
#define HAT 20000
#define BASS 500
#define SNARE 6000
#define SUB 50
static const int16_t notes[5][SONG_LENGTH] = {
{ // melody notes
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 262, 0, 294, 0, 392, 0, 440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},
{ // rhythm notes
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
},
{ // drum beats
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0
},
{ // hi-hat
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1
},
{ // bass notes under bass drum
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0
},
};
namespace pimoroni {
GalacticUnicorn* GalacticUnicorn::unicorn = nullptr;
@ -96,20 +60,15 @@ namespace pimoroni {
void __isr GalacticUnicorn::dma_complete() {
if(unicorn != nullptr) {
if(dma_channel_get_irq0_status(dma_channel)) {
gpio_put(I2C_SCL, true);
unicorn->next_dma_sequence();
gpio_put(I2C_SCL, false);
unicorn->next_bitstream_sequence();
}
if(dma_channel_get_irq0_status(audio_dma_channel)) {
gpio_put(I2C_SDA, true);
unicorn->loop_tone();
gpio_put(I2C_SDA, false);
unicorn->next_audio_sequence();
}
}
}
void GalacticUnicorn::next_dma_sequence() {
void GalacticUnicorn::next_bitstream_sequence() {
// Clear any interrupt request caused by our channel
//dma_channel_acknowledge_irq0(dma_channel);
// NOTE Temporary replacement of the above until this reaches pico-sdk main:
@ -213,10 +172,6 @@ namespace pimoroni {
}
}
// temp for debugging
gpio_init(I2C_SDA); gpio_set_dir(I2C_SDA, GPIO_OUT); gpio_put(I2C_SDA, false);
gpio_init(I2C_SCL); gpio_set_dir(I2C_SCL, GPIO_OUT); gpio_put(I2C_SCL, false);
// setup light sensor adc
adc_init();
adc_gpio_init(LIGHT_SENSOR);
@ -384,60 +339,9 @@ namespace pimoroni {
irq_set_enabled(DMA_IRQ_0, true);
}
// configure voices
/*
channels[0].waveforms = Waveform::SINE;
channels[0].attack_ms = 1;
channels[0].decay_ms = 1;
channels[0].sustain = 0xafff;
channels[0].release_ms = 1;
channels[0].volume = 10000;
channels[0].frequency = 1000;
*/
// melody track
channels[0].waveforms = Waveform::TRIANGLE | Waveform::SQUARE;
channels[0].attack_ms = 16;
channels[0].decay_ms = 168;
channels[0].sustain = 0xafff;
channels[0].release_ms = 168;
channels[0].volume = 10000;
// rhythm track
channels[1].waveforms = Waveform::SINE | Waveform::SQUARE;
channels[1].attack_ms = 38;
channels[1].decay_ms = 300;
channels[1].sustain = 0;
channels[1].release_ms = 0;
channels[1].volume = 12000;
// drum track
channels[2].waveforms = Waveform::NOISE;
channels[2].attack_ms = 5;
channels[2].decay_ms = 10;
channels[2].sustain = 16000;
channels[2].release_ms = 100;
channels[2].volume = 18000;
// hi-hat track
channels[3].waveforms = Waveform::NOISE;
channels[3].attack_ms = 5;
channels[3].decay_ms = 5;
channels[3].sustain = 8000;
channels[3].release_ms = 40;
channels[3].volume = 8000;
// bass track
channels[4].waveforms = Waveform::SQUARE;
channels[4].attack_ms = 10;
channels[4].decay_ms = 100;
channels[4].sustain = 0;
channels[4].release_ms = 500;
channels[4].volume = 12000;
unicorn = this;
next_dma_sequence();
next_bitstream_sequence();
}
void GalacticUnicorn::clear() {
@ -478,137 +382,49 @@ namespace pimoroni {
}
}
void GalacticUnicorn::play_tone(float frequency) {
play_dual_tone(frequency, 0.0f);
}
void GalacticUnicorn::play_dual_tone(float freq_a, float freq_b) {
if(play_mode != PLAYING_TONE) {
stop_playing();
}
if(unicorn == this) {
tone_frequency_a = MAX(freq_a, 0.0f);
tone_frequency_b = MAX(freq_b, 0.0f);
if(play_mode == NOT_PLAYING) {
// Nothing is playing, so we can set up the first buffer straight away
current_buffer = 0;
populate_next_tone();
// Restart the audio SM and start a new DMA transfer
pio_sm_set_enabled(audio_pio, audio_sm, true);
play_mode = PLAYING_TONE;
loop_tone();
}
}
}
void GalacticUnicorn::play_synth() {
if(play_mode != PLAYING_SYNTH) {
stop_playing();
}
if(unicorn == this) {
if(unicorn == this && play_mode == NOT_PLAYING) {
// Nothing is playing, so we can set up the first buffer straight away
current_buffer = 0;
if(play_mode == NOT_PLAYING) {
// Nothing is playing, so we can set up the first buffer straight away
current_buffer = 0;
populate_next_synth();
absolute_time_t at = get_absolute_time();
synth_start = to_us_since_boot(at) / 1000;
// Restart the audio SM and start a new DMA transfer
pio_sm_set_enabled(audio_pio, audio_sm, true);
populate_next_synth();
play_mode = PLAYING_SYNTH;
// Restart the audio SM and start a new DMA transfer
pio_sm_set_enabled(audio_pio, audio_sm, true);
play_mode = PLAYING_SYNTH;
loop_tone();
}
next_audio_sequence();
}
}
void GalacticUnicorn::loop_tone() {
void GalacticUnicorn::next_audio_sequence() {
// Clear any interrupt request caused by our channel
//dma_channel_acknowledge_irq0(audio_dma_channel);
// NOTE Temporary replacement of the above until this reaches pico-sdk main:
// https://github.com/raspberrypi/pico-sdk/issues/974
dma_hw->ints0 = 1u << audio_dma_channel;
if(play_mode == PLAYING_TONE || play_mode == PLAYING_SYNTH) {
if(play_mode == PLAYING_SYNTH) {
dma_channel_transfer_from_buffer_now(audio_dma_channel, tone_buffers[current_buffer], TONE_BUFFER_SIZE);
current_buffer = (current_buffer + 1) % NUM_TONE_BUFFERS;
switch(play_mode) {
case PLAYING_TONE:
populate_next_tone();
break;
case PLAYING_SYNTH:
populate_next_synth();
break;
default:
break;
}
populate_next_synth();
}
else {
play_mode = NOT_PLAYING;
}
}
void GalacticUnicorn::populate_next_tone() {
float percent_along_a, percent_along_b;
int16_t val;
int16_t* buffer = tone_buffers[current_buffer];
//sine_waveform
for(uint i = 0; i < TONE_BUFFER_SIZE; i++) {
percent_along_a = (((float)i * tone_frequency_a) / SYSTEM_FREQ) + wave_start_a;
val = (sine_waveform[(int)(percent_along_a * 256.0f) % 256] * 0x0fff) >> 16;
//val = sinf(percent_along_a * 2 * M_PI) * 0x0fff;
percent_along_b = (((float)i * tone_frequency_b) / SYSTEM_FREQ) + wave_start_b;
val += (sine_waveform[(int)(percent_along_b * 256.0f) % 256] * 0x0fff) >> 16;
//val += sinf(percent_along_b * 2 * M_PI) * 0x0fff;
buffer[i] = val;
}
wave_start_a = fmodf((((float)TONE_BUFFER_SIZE * tone_frequency_a) / SYSTEM_FREQ) + wave_start_a, 1.0f);
wave_start_b = fmodf((((float)TONE_BUFFER_SIZE * tone_frequency_b) / SYSTEM_FREQ) + wave_start_b, 1.0f);
}
void GalacticUnicorn::populate_next_synth() {
static uint16_t prev_beat = 1;
static uint16_t beat = 0;
absolute_time_t at = get_absolute_time();
uint64_t tick_ms = (to_us_since_boot(at) / 1000) - synth_start;
beat = (tick_ms / 100) % SONG_LENGTH; // 100ms per beat
if (beat != prev_beat) {
prev_beat = beat;
for(uint8_t i = 0; i < 5; i++) {
if(notes[i][beat] > 0) {
channels[i].frequency = notes[i][beat];
channels[i].trigger_attack();
} else if (notes[i][beat] == -1) {
channels[i].trigger_release();
}
}
}
int16_t *samples = tone_buffers[current_buffer];
for(uint i = 0; i < TONE_BUFFER_SIZE; i++) {
samples[i] = get_audio_frame(channels);
samples[i] = synth.get_audio_frame();
}
}

Wyświetl plik

@ -2,9 +2,7 @@
#include "hardware/pio.h"
#include "pico_graphics.hpp"
#include "synth.hpp"
using namespace synth;
#include "../pico_synth/pico_synth.hpp"
namespace pimoroni {
@ -77,16 +75,13 @@ namespace pimoroni {
int16_t tone_buffers[NUM_TONE_BUFFERS][TONE_BUFFER_SIZE] = {0};
uint current_buffer = 0;
float tone_frequency_a = 0.0f;
float tone_frequency_b = 0.0f;
float wave_start_a = 0.0f;
float wave_start_b = 0.0f;
synth::AudioChannel channels[CHANNEL_COUNT];
public:
PicoSynth synth;
private:
enum PlayMode {
PLAYING_BUFFER,
PLAYING_TONE,
//PLAYING_TONE,
PLAYING_SYNTH,
NOT_PLAYING
};
@ -119,19 +114,14 @@ namespace pimoroni {
bool is_pressed(uint8_t button);
void play_sample(uint8_t *data, uint32_t length);
void play_tone(float frequency);
void play_dual_tone(float freq_a, float freq_b);
void play_synth();
void stop_playing();
uint64_t synth_start = 0;
private:
void next_dma_sequence();
void next_bitstream_sequence();
void partial_teardown();
void dma_safe_abort(uint channel);
void loop_tone();
void populate_next_tone();
void next_audio_sequence();
void populate_next_synth();
};

Wyświetl plik

@ -1,6 +1,6 @@
#include "synth.hpp"
#include "pico_synth.hpp"
namespace synth {
namespace pimoroni {
uint32_t prng_xorshift_state = 0x32B71700;
@ -21,16 +21,48 @@ namespace synth {
return n - 0xffff;
}
//uint16_t volume = 0xffff;
const int16_t sine_waveform[256] = {-32768,-32758,-32729,-32679,-32610,-32522,-32413,-32286,-32138,-31972,-31786,-31581,-31357,-31114,-30853,-30572,-30274,-29957,-29622,-29269,-28899,-28511,-28106,-27684,-27246,-26791,-26320,-25833,-25330,-24812,-24279,-23732,-23170,-22595,-22006,-21403,-20788,-20160,-19520,-18868,-18205,-17531,-16846,-16151,-15447,-14733,-14010,-13279,-12540,-11793,-11039,-10279,-9512,-8740,-7962,-7180,-6393,-5602,-4808,-4011,-3212,-2411,-1608,-804,0,804,1608,2411,3212,4011,4808,5602,6393,7180,7962,8740,9512,10279,11039,11793,12540,13279,14010,14733,15447,16151,16846,17531,18205,18868,19520,20160,20788,21403,22006,22595,23170,23732,24279,24812,25330,25833,26320,26791,27246,27684,28106,28511,28899,29269,29622,29957,30274,30572,30853,31114,31357,31581,31786,31972,32138,32286,32413,32522,32610,32679,32729,32758,32767,32758,32729,32679,32610,32522,32413,32286,32138,31972,31786,31581,31357,31114,30853,30572,30274,29957,29622,29269,28899,28511,28106,27684,27246,26791,26320,25833,25330,24812,24279,23732,23170,22595,22006,21403,20788,20160,19520,18868,18205,17531,16846,16151,15447,14733,14010,13279,12540,11793,11039,10279,9512,8740,7962,7180,6393,5602,4808,4011,3212,2411,1608,804,0,-804,-1608,-2411,-3212,-4011,-4808,-5602,-6393,-7180,-7962,-8740,-9512,-10279,-11039,-11793,-12540,-13279,-14010,-14733,-15447,-16151,-16846,-17531,-18205,-18868,-19520,-20160,-20788,-21403,-22006,-22595,-23170,-23732,-24279,-24812,-25330,-25833,-26320,-26791,-27246,-27684,-28106,-28511,-28899,-29269,-29622,-29957,-30274,-30572,-30853,-31114,-31357,-31581,-31786,-31972,-32138,-32286,-32413,-32522,-32610,-32679,-32729,-32758};
bool is_audio_playing(AudioChannel *channels) {
void AudioChannel::trigger_attack() {
adsr_frame = 0;
adsr_phase = ADSRPhase::ATTACK;
adsr_end_frame = (attack_ms * sample_rate) / 1000;
adsr_step = (int32_t(0xffffff) - int32_t(adsr_level)) / int32_t(adsr_end_frame);
}
void AudioChannel::trigger_decay() {
adsr_frame = 0;
adsr_phase = ADSRPhase::DECAY;
adsr_end_frame = (decay_ms * sample_rate) / 1000;
adsr_step = (int32_t(sustain << 8) - int32_t(adsr_level)) / int32_t(adsr_end_frame);
}
void AudioChannel::trigger_sustain() {
adsr_frame = 0;
adsr_phase = ADSRPhase::SUSTAIN;
adsr_end_frame = 0;
adsr_step = 0;
}
void AudioChannel::trigger_release() {
adsr_frame = 0;
adsr_phase = ADSRPhase::RELEASE;
adsr_end_frame = (release_ms * sample_rate) / 1000;
adsr_step = (int32_t(0) - int32_t(adsr_level)) / int32_t(adsr_end_frame);
}
void AudioChannel::off() {
adsr_frame = 0;
adsr_phase = ADSRPhase::OFF;
adsr_step = 0;
}
bool PicoSynth::is_audio_playing() {
if(volume == 0) {
return false;
}
bool any_channel_playing = false;
for(int c = 0; c < CHANNEL_COUNT; c++) {
for(uint c = 0; c < CHANNEL_COUNT; c++) {
if(channels[c].volume > 0 && channels[c].adsr_phase != ADSRPhase::OFF) {
any_channel_playing = true;
}
@ -39,10 +71,10 @@ namespace synth {
return any_channel_playing;
}
int16_t get_audio_frame(AudioChannel *channels) {
int16_t PicoSynth::get_audio_frame() {
int32_t sample = 0; // used to combine channel output
for(int c = 0; c < CHANNEL_COUNT; c++) {
for(uint c = 0; c < CHANNEL_COUNT; c++) {
auto &channel = channels[c];
@ -55,7 +87,7 @@ namespace synth {
continue;
}
if ((channel.adsr_frame >= channel.adsr_end_frame) && (channel.adsr_phase != ADSRPhase::SUSTAIN)) {
if((channel.adsr_frame >= channel.adsr_end_frame) && (channel.adsr_phase != ADSRPhase::SUSTAIN)) {
switch (channel.adsr_phase) {
case ADSRPhase::ATTACK:
channel.trigger_decay();
@ -71,7 +103,7 @@ namespace synth {
}
}
channel.adsr += channel.adsr_step;
channel.adsr_level += channel.adsr_step;
channel.adsr_frame++;
if(channel.waveform_offset & 0x10000) {
@ -98,8 +130,8 @@ namespace synth {
}
// creates a triangle wave of ^
if (channel.waveforms & Waveform::TRIANGLE) {
if (channel.waveform_offset < 0x7fff) { // initial quarter up slope
if(channel.waveforms & Waveform::TRIANGLE) {
if(channel.waveform_offset < 0x7fff) { // initial quarter up slope
channel_sample += int32_t(channel.waveform_offset * 2) - int32_t(0x7fff);
}
else { // final quarter up slope
@ -108,7 +140,7 @@ namespace synth {
waveform_count++;
}
if (channel.waveforms & Waveform::SQUARE) {
if(channel.waveforms & Waveform::SQUARE) {
channel_sample += (channel.waveform_offset < channel.pulse_width) ? 0x7fff : -0x7fff;
waveform_count++;
}
@ -123,17 +155,17 @@ namespace synth {
if(channel.waveforms & Waveform::WAVE) {
channel_sample += channel.wave_buffer[channel.wave_buf_pos];
if (++channel.wave_buf_pos == 64) {
if(++channel.wave_buf_pos == 64) {
channel.wave_buf_pos = 0;
if(channel.wave_buffer_callback)
channel.wave_buffer_callback(channel);
channel.wave_buffer_callback(channel);
}
waveform_count++;
}
channel_sample = channel_sample / waveform_count;
channel_sample = (int64_t(channel_sample) * int32_t(channel.adsr >> 8)) >> 16;
channel_sample = (int64_t(channel_sample) * int32_t(channel.adsr_level >> 8)) >> 16;
// apply channel volume
channel_sample = (int64_t(channel_sample) * int32_t(channel.volume)) >> 16;
@ -157,4 +189,5 @@ namespace synth {
sample = sample <= -0x8000 ? -0x8000 : (sample > 0x7fff ? 0x7fff : sample);
return sample;
}
}
}

Wyświetl plik

@ -1,8 +1,15 @@
#pragma once
#include <cstdint>
//#include <string>
//#include <array>
//#include <cstdint>
//#include <algorithm>
//#include <vector>
//#include <functional>
namespace synth {
#include "common/pimoroni_common.hpp"
namespace pimoroni {
// The duration a note is played is determined by the amount of attack,
// decay, and release, combined with the length of the note as defined by
@ -39,13 +46,6 @@ namespace synth {
// |X | | | | | | | | | | | | | | | | |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--->
#define CHANNEL_COUNT 8
constexpr float pi = 3.14159265358979323846f;
const uint32_t sample_rate = 22050;
const uint16_t volume = 0x2fff;
enum Waveform {
NOISE = 128,
SQUARE = 64,
@ -63,13 +63,16 @@ namespace synth {
OFF
};
struct AudioChannel {
uint8_t waveforms = 0; // bitmask for enabled waveforms (see AudioWaveform enum for values)
uint16_t frequency = 660; // frequency of the voice (Hz)
uint16_t volume = 0xffff; // channel volume (default 50%)
const int16_t sine_waveform[256] = {-32768,-32758,-32729,-32679,-32610,-32522,-32413,-32286,-32138,-31972,-31786,-31581,-31357,-31114,-30853,-30572,-30274,-29957,-29622,-29269,-28899,-28511,-28106,-27684,-27246,-26791,-26320,-25833,-25330,-24812,-24279,-23732,-23170,-22595,-22006,-21403,-20788,-20160,-19520,-18868,-18205,-17531,-16846,-16151,-15447,-14733,-14010,-13279,-12540,-11793,-11039,-10279,-9512,-8740,-7962,-7180,-6393,-5602,-4808,-4011,-3212,-2411,-1608,-804,0,804,1608,2411,3212,4011,4808,5602,6393,7180,7962,8740,9512,10279,11039,11793,12540,13279,14010,14733,15447,16151,16846,17531,18205,18868,19520,20160,20788,21403,22006,22595,23170,23732,24279,24812,25330,25833,26320,26791,27246,27684,28106,28511,28899,29269,29622,29957,30274,30572,30853,31114,31357,31581,31786,31972,32138,32286,32413,32522,32610,32679,32729,32758,32767,32758,32729,32679,32610,32522,32413,32286,32138,31972,31786,31581,31357,31114,30853,30572,30274,29957,29622,29269,28899,28511,28106,27684,27246,26791,26320,25833,25330,24812,24279,23732,23170,22595,22006,21403,20788,20160,19520,18868,18205,17531,16846,16151,15447,14733,14010,13279,12540,11793,11039,10279,9512,8740,7962,7180,6393,5602,4808,4011,3212,2411,1608,804,0,-804,-1608,-2411,-3212,-4011,-4808,-5602,-6393,-7180,-7962,-8740,-9512,-10279,-11039,-11793,-12540,-13279,-14010,-14733,-15447,-16151,-16846,-17531,-18205,-18868,-19520,-20160,-20788,-21403,-22006,-22595,-23170,-23732,-24279,-24812,-25330,-25833,-26320,-26791,-27246,-27684,-28106,-28511,-28899,-29269,-29622,-29957,-30274,-30572,-30853,-31114,-31357,-31581,-31786,-31972,-32138,-32286,-32413,-32522,-32610,-32679,-32729,-32758};
const uint32_t sample_rate = 22050;
uint16_t attack_ms = 2; // attack period
uint16_t decay_ms = 6; // decay period
struct AudioChannel {
uint8_t waveforms = 0; // bitmask for enabled waveforms (see Waveform enum for values)
uint16_t frequency = 660; // frequency of the voice (Hz)
uint16_t volume = 0xffff; // channel volume
uint16_t attack_ms = 2; // attack period (cannot be zero)
uint16_t decay_ms = 6; // decay period (cannot be zero)
uint16_t sustain = 0xffff; // sustain volume
uint16_t release_ms = 1; // release period
uint16_t pulse_width = 0x7fff; // duty cycle of square wave (default 50%)
@ -81,11 +84,11 @@ namespace synth {
bool filter_enable = false;
uint16_t filter_cutoff_frequency = 0;
uint32_t adsr_frame = 0; // number of frames into the current ADSR phase
uint32_t adsr_frame = 0; // number of frames into the current ADSR phase
uint32_t adsr_end_frame = 0; // frame target at which the ADSR changes to the next phase
uint32_t adsr = 0;
int32_t adsr_step = 0;
ADSRPhase adsr_phase = ADSRPhase::OFF;
uint32_t adsr_level = 0; // the output level at the current frame of the ADSR phase
int32_t adsr_step = 0; // the amount to increment the level with each frame
ADSRPhase adsr_phase = ADSRPhase::OFF;
uint8_t wave_buf_pos = 0; //
int16_t wave_buffer[64]; // buffer for arbitrary waveforms. small as it's filled by user callback
@ -93,40 +96,24 @@ namespace synth {
void *user_data = nullptr;
void (*wave_buffer_callback)(AudioChannel &channel);
void trigger_attack() {
adsr_frame = 0;
adsr_phase = ADSRPhase::ATTACK;
adsr_end_frame = (attack_ms * sample_rate) / 1000;
adsr_step = (int32_t(0xffffff) - int32_t(adsr)) / int32_t(adsr_end_frame);
}
void trigger_decay() {
adsr_frame = 0;
adsr_phase = ADSRPhase::DECAY;
adsr_end_frame = (decay_ms * sample_rate) / 1000;
adsr_step = (int32_t(sustain << 8) - int32_t(adsr)) / int32_t(adsr_end_frame);
}
void trigger_sustain() {
adsr_frame = 0;
adsr_phase = ADSRPhase::SUSTAIN;
adsr_end_frame = 0;
adsr_step = 0;
}
void trigger_release() {
adsr_frame = 0;
adsr_phase = ADSRPhase::RELEASE;
adsr_end_frame = (release_ms * sample_rate) / 1000;
adsr_step = (int32_t(0) - int32_t(adsr)) / int32_t(adsr_end_frame);
}
void off() {
adsr_frame = 0;
adsr_phase = ADSRPhase::OFF;
adsr_step = 0;
}
void trigger_attack();
void trigger_decay();
void trigger_sustain();
void trigger_release();
void off();
};
//extern AudioChannel channels[CHANNEL_COUNT];
class PicoSynth {
public:
const uint16_t volume = 0x2fff;
int16_t get_audio_frame(AudioChannel *channels);
bool is_audio_playing(AudioChannel *channels);
static const uint CHANNEL_COUNT = 8;
AudioChannel channels[CHANNEL_COUNT];
}
int16_t get_audio_frame();
bool is_audio_playing();
};
constexpr float pi = 3.14159265358979323846f;
}

Wyświetl plik

@ -1,6 +1,7 @@
import gc
import time
import math
from machine import Timer
from galactic import GalacticUnicorn
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
@ -611,6 +612,39 @@ left_channel = bytearray((
0xa0, 0xfe, 0x6c, 0xfb, 0x59, 0xf8, 0x5a, 0xf8, 0xd0, 0xf8, 0xba, 0xfd,
))
SONG_LENGTH = 384
HAT = 20000
BASS = 500
SNARE = 6000
SUB = 50
melody_notes = (
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 262, 0, 294, 0, 392, 0, 440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
rhythm_notes = (
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0)
drum_beats = (
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0)
hi_hat = (
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1)
bass_notes = (
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0)
notes = [melody_notes, rhythm_notes, drum_beats, hi_hat, bass_notes]
def gradient(r, g, b):
for y in range(0, height):
@ -664,6 +698,30 @@ bool_playing = False
freq_a = 0
freq_b = 0
beat = 0
def next_beat():
global beat
for i in range(5):
if notes[i][beat] > 0:
gu.channel_freq(i, notes[i][beat])
gu.channel_trigger_attack(i)
elif notes[i][beat] == -1:
gu.channel_trigger_release(i)
beat = (beat + 1) % SONG_LENGTH
def tick(timer):
next_beat()
timer = Timer(-1)
synthing = False
while True:
time_ms = time.ticks_ms()
@ -671,26 +729,100 @@ while True:
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
if not was_a_pressed:
gu.play_sample(left_channel)
bool_playing = False
gu.channel_configure(0, gu.WF_TRIANGLE + gu.WF_SQUARE,
16,
168,
0,
168,
0)
gu.channel_configure(1, gu.WF_SINE + gu.WF_SQUARE,
38,
300,
0,
0,
12000)
gu.channel_configure(2, gu.WF_NOISE,
5,
10,
16000,
100,
0)
gu.channel_configure(3, gu.WF_NOISE,
5,
5,
8000,
40,
0)
gu.channel_configure(4, gu.WF_SQUARE,
10,
100,
0,
500,
0)
if not synthing:
beat = 0
next_beat()
gu.play_synth()
synthing = True
timer.init(freq=10, mode=Timer.PERIODIC, callback=tick)
was_a_pressed = True
else:
was_a_pressed = False
if gu.is_pressed(GalacticUnicorn.SWITCH_B):
if not was_b_pressed:
timer.deinit()
freq_a = 400
gu.play_dual_tone(freq_a, freq_b)
gu.channel_freq(0, freq_a)
gu.channel_configure(0, gu.WF_SINE,
1,
1,
0xffff,
1,
4000)
gu.channel_configure(1, gu.WF_SINE,
1,
1,
0xffff,
1,
4000)
gu.channel_trigger_attack(0)
gu.play_synth()
synthing = False
bool_playing = True
was_b_pressed = True
else:
was_b_pressed = False
if gu.is_pressed(GalacticUnicorn.SWITCH_C):
if not was_c_pressed:
timer.deinit()
freq_b = 600
gu.play_dual_tone(freq_a, freq_b)
gu.channel_freq(1, freq_b)
gu.channel_configure(0, gu.WF_SINE,
1,
1,
0xffff,
1,
4000)
gu.channel_configure(1, gu.WF_SINE,
1,
1,
0xffff,
1,
4000)
gu.channel_trigger_attack(1)
gu.play_synth()
synthing = False
bool_playing = True
was_c_pressed = True
else:
was_c_pressed = False
@ -700,6 +832,8 @@ while True:
freq_a = 0
freq_b = 0
gu.stop_playing()
timer.deinit()
synthing = False
was_d_pressed = True
else:
was_d_pressed = False
@ -708,27 +842,62 @@ while True:
# gu.adjust_brightness(+0.01)
if bool_playing:
freq_b += 10
gu.play_dual_tone(freq_a, freq_b)
gu.channel_freq(1, freq_b)
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
# gu.adjust_brightness(-0.01)
if bool_playing:
freq_a -= 10
gu.play_dual_tone(freq_a, freq_b)
freq_b -= 10
gu.channel_freq(1, freq_b)
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
if bool_playing:
freq_a += 10
gu.play_dual_tone(freq_a, freq_b)
gu.channel_freq(0, freq_a)
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
if bool_playing:
freq_a -= 10
gu.play_dual_tone(freq_a, freq_b)
gu.channel_freq(0, freq_a)
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
if not was_z_pressed:
gu.channel_configure(0, gu.WF_TRIANGLE + gu.WF_SQUARE,
16,
168,
0xafff,
168,
10000)
gu.channel_configure(1, gu.WF_SINE + gu.WF_SQUARE,
38,
300,
0,
0,
12000)
gu.channel_configure(2, gu.WF_NOISE,
5,
10,
16000,
100,
18000)
gu.channel_configure(3, gu.WF_NOISE,
5,
5,
8000,
40,
8000)
gu.channel_configure(4, gu.WF_SQUARE,
10,
100,
0,
500,
12000)
if not synthing:
beat = 0
next_beat()
gu.play_synth()
synthing = True
timer.init(freq=10, mode=Timer.PERIODIC, callback=tick)
was_z_pressed = True
else:
was_z_pressed = False

Wyświetl plik

@ -14,10 +14,12 @@ MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_adjust_volume_obj, GalacticUnicorn_adj
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_light_obj, GalacticUnicorn_light);
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_is_pressed_obj, GalacticUnicorn_is_pressed);
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_play_sample_obj, GalacticUnicorn_play_sample);
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_play_tone_obj, GalacticUnicorn_play_tone);
MP_DEFINE_CONST_FUN_OBJ_3(GalacticUnicorn_play_dual_tone_obj, GalacticUnicorn_play_dual_tone);
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_play_synth_obj, GalacticUnicorn_play_synth);
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_stop_playing_obj, GalacticUnicorn_stop_playing);
MP_DEFINE_CONST_FUN_OBJ_KW(GalacticUnicorn_channel_configure_obj, 7, GalacticUnicorn_channel_configure);
MP_DEFINE_CONST_FUN_OBJ_3(GalacticUnicorn_channel_freq_obj, GalacticUnicorn_channel_freq);
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_channel_trigger_attack_obj, GalacticUnicorn_channel_trigger_attack);
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_channel_trigger_release_obj, GalacticUnicorn_channel_trigger_release);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = {
@ -33,10 +35,12 @@ STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_light), MP_ROM_PTR(&GalacticUnicorn_light_obj) },
{ MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&GalacticUnicorn_is_pressed_obj) },
{ MP_ROM_QSTR(MP_QSTR_play_sample), MP_ROM_PTR(&GalacticUnicorn_play_sample_obj) },
{ MP_ROM_QSTR(MP_QSTR_play_tone), MP_ROM_PTR(&GalacticUnicorn_play_tone_obj) },
{ MP_ROM_QSTR(MP_QSTR_play_dual_tone), MP_ROM_PTR(&GalacticUnicorn_play_dual_tone_obj) },
{ MP_ROM_QSTR(MP_QSTR_play_synth), MP_ROM_PTR(&GalacticUnicorn_play_synth_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_playing), MP_ROM_PTR(&GalacticUnicorn_stop_playing_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_configure), MP_ROM_PTR(&GalacticUnicorn_channel_configure_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_freq), MP_ROM_PTR(&GalacticUnicorn_channel_freq_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_trigger_attack), MP_ROM_PTR(&GalacticUnicorn_channel_trigger_attack_obj) },
{ MP_ROM_QSTR(MP_QSTR_channel_trigger_release), MP_ROM_PTR(&GalacticUnicorn_channel_trigger_release_obj) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(53) },
{ MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(11) },
@ -50,6 +54,13 @@ STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SWITCH_VOLUME_DOWN), MP_ROM_INT(8) },
{ MP_ROM_QSTR(MP_QSTR_SWITCH_BRIGHTNESS_UP), MP_ROM_INT(21) },
{ MP_ROM_QSTR(MP_QSTR_SWITCH_BRIGHTNESS_DOWN), MP_ROM_INT(26) },
{ MP_ROM_QSTR(MP_QSTR_WF_NOISE), MP_ROM_INT(128) },
{ MP_ROM_QSTR(MP_QSTR_WF_SQUARE), MP_ROM_INT(64) },
{ MP_ROM_QSTR(MP_QSTR_WF_SAW), MP_ROM_INT(32) },
{ MP_ROM_QSTR(MP_QSTR_WF_TRIANGLE), MP_ROM_INT(16) },
{ MP_ROM_QSTR(MP_QSTR_WF_SINE), MP_ROM_INT(8) },
{ MP_ROM_QSTR(MP_QSTR_WF_WAVE), MP_ROM_INT(1) },
};
STATIC MP_DEFINE_CONST_DICT(GalacticUnicorn_locals_dict, GalacticUnicorn_locals_dict_table);

Wyświetl plik

@ -159,20 +159,6 @@ extern mp_obj_t GalacticUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data) {
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_play_tone(mp_obj_t self_in, mp_obj_t freq) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->play_tone(mp_obj_get_float(freq));
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_play_dual_tone(mp_obj_t self_in, mp_obj_t freq_a, mp_obj_t freq_b) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->play_dual_tone(mp_obj_get_float(freq_a), mp_obj_get_float(freq_b));
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_play_synth(mp_obj_t self_in) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->play_synth();
@ -186,4 +172,56 @@ extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in) {
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_channel, ARG_waveforms, ARG_attack_ms, ARG_decay_ms, ARG_sustain, ARG_release_ms, ARG_volume };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_channel, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_waveforms, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_attack_ms, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_decay_ms, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_sustain, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_release_ms, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_volumes, MP_ARG_REQUIRED | MP_ARG_INT },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _GalacticUnicorn_obj_t);
int c = args[ARG_channel].u_int;
self->galactic->synth.channels[c].waveforms = args[ARG_waveforms].u_int;
self->galactic->synth.channels[c].attack_ms = args[ARG_attack_ms].u_int;
self->galactic->synth.channels[c].decay_ms = args[ARG_decay_ms].u_int;
self->galactic->synth.channels[c].sustain = args[ARG_sustain].u_int;
self->galactic->synth.channels[c].release_ms = args[ARG_release_ms].u_int;
self->galactic->synth.channels[c].volume = args[ARG_volume].u_int;
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_channel_freq(mp_obj_t self_in, mp_obj_t channel, mp_obj_t freq) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->synth.channels[mp_obj_get_int(channel)].frequency = mp_obj_get_float(freq);
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_channel_trigger_attack(mp_obj_t self_in, mp_obj_t channel) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->synth.channels[mp_obj_get_int(channel)].trigger_attack();
return mp_const_none;
}
extern mp_obj_t GalacticUnicorn_channel_trigger_release(mp_obj_t self_in, mp_obj_t channel) {
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
self->galactic->synth.channels[mp_obj_get_int(channel)].trigger_release();
return mp_const_none;
}
}

Wyświetl plik

@ -25,7 +25,10 @@ extern mp_obj_t GalacticUnicorn_light(mp_obj_t self_in);
extern mp_obj_t GalacticUnicorn_is_pressed(mp_obj_t self_in, mp_obj_t button);
extern mp_obj_t GalacticUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data);
extern mp_obj_t GalacticUnicorn_play_tone(mp_obj_t self_in, mp_obj_t freq);
extern mp_obj_t GalacticUnicorn_play_dual_tone(mp_obj_t self_in, mp_obj_t freq_a, mp_obj_t freq_b);
extern mp_obj_t GalacticUnicorn_play_synth(mp_obj_t self_in);
extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in);
extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in);
extern mp_obj_t GalacticUnicorn_channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);;
extern mp_obj_t GalacticUnicorn_channel_freq(mp_obj_t self_in, mp_obj_t channel, mp_obj_t freq);
extern mp_obj_t GalacticUnicorn_channel_trigger_attack(mp_obj_t self_in, mp_obj_t channel);
extern mp_obj_t GalacticUnicorn_channel_trigger_release(mp_obj_t self_in, mp_obj_t channel);

Wyświetl plik

@ -6,6 +6,7 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/galactic_unicorn/galactic_unicorn.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_synth/pico_synth.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb888.cpp
)
pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/galactic_unicorn/galactic_unicorn.pio)