kopia lustrzana https://github.com/pimoroni/pimoroni-pico
				
				
				
			More work on GU synth
							rodzic
							
								
									deec835692
								
							
						
					
					
						commit
						882f76dcbc
					
				| 
						 | 
				
			
			@ -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})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue