kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Merge pull request #537 from pimoroni/feature/galactic_unicorn
Galactic Unicornpull/540/head v1.19.9
commit
e928129d59
|
@ -61,7 +61,7 @@ jobs:
|
|||
|
||||
build:
|
||||
needs: deps
|
||||
name: Build ${{matrix.board}}
|
||||
name: Build ${{matrix.name}} (${{matrix.board}})
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
|
@ -70,6 +70,8 @@ jobs:
|
|||
board: PICO_W
|
||||
- name: picow_enviro
|
||||
board: PICO_W_ENVIRO
|
||||
- name: picow_galactic_unicorn
|
||||
board: PICO_W
|
||||
|
||||
env:
|
||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||
|
|
|
@ -56,3 +56,4 @@ add_subdirectory(servo2040)
|
|||
add_subdirectory(motor2040)
|
||||
add_subdirectory(inventor2040w)
|
||||
add_subdirectory(encoder)
|
||||
add_subdirectory(galactic_unicorn)
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
add_executable(
|
||||
rainbow_text
|
||||
rainbow_text.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(rainbow_text pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(rainbow_text 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(rainbow_text)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
rainbow
|
||||
rainbow.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(rainbow pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(rainbow 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(rainbow)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
nostalgia_prompt
|
||||
nostalgia_prompt.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(nostalgia_prompt pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(nostalgia_prompt 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(nostalgia_prompt)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
eighties_super_computer
|
||||
eighties_super_computer.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(eighties_super_computer pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(eighties_super_computer 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(eighties_super_computer)
|
||||
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
fire_effect
|
||||
fire_effect.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(fire_effect pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(fire_effect 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(fire_effect)
|
||||
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
scroll_text
|
||||
scroll_text.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(scroll_text pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(scroll_text 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(scroll_text)
|
||||
|
||||
|
||||
add_executable(
|
||||
balls
|
||||
balls.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(balls pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(balls 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(balls)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
lava_lamp
|
||||
lava_lamp.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(lava_lamp pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(lava_lamp 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(lava_lamp)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
feature_test
|
||||
feature_test.cpp
|
||||
audio_samples.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(feature_test pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
|
||||
pico_enable_stdio_usb(feature_test 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(feature_test)
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,87 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// extra row of pixels for sourcing flames and averaging
|
||||
float heat[53][13] = {0.0f};
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
while(true) {
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
for(int y = 0; y < 12; y++) {
|
||||
for(int x = 0; x < 53; x++) {
|
||||
if(heat[x][y] > 0.5f) {
|
||||
graphics.set_pen(255, 255, 180);
|
||||
graphics.pixel(Point(x, y));
|
||||
}else if(heat[x][y] > 0.4f) {
|
||||
graphics.set_pen(220, 160, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}else if(heat[x][y] > 0.3f) {
|
||||
graphics.set_pen(180, 50, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}else if(heat[x][y] > 0.2f) {
|
||||
graphics.set_pen(40, 40, 40);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
|
||||
// update this pixel by averaging the below pixels
|
||||
if(x == 0) {
|
||||
heat[x][y] = (heat[x][y] + heat[x][y + 2] + heat[x][y + 1] + heat[x + 1][y + 1]) / 4.0f;
|
||||
} else if(x == 52) {
|
||||
heat[x][y] = (heat[x][y] + heat[x][y + 2] + heat[x][y + 1] + heat[x - 1][y + 1]) / 4.0f;
|
||||
} else {
|
||||
heat[x][y] = (heat[x][y] + heat[x][y + 2] + heat[x][y + 1] + heat[x - 1][y + 1] + heat[x + 1][y + 1]) / 5.0f;
|
||||
}
|
||||
|
||||
heat[x][y] -= 0.01f;
|
||||
heat[x][y] = heat[x][y] < 0.0f ? 0.0f: heat[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
// clear the bottom row and then add a new fire seed to it
|
||||
for(int x = 0; x < 53; x++) {
|
||||
heat[x][11] = 0.0f;
|
||||
}
|
||||
|
||||
// add a new random heat source
|
||||
for(int c = 0; c < 5; c++) {
|
||||
int px = (rand() % 51) + 1;
|
||||
heat[px][11] = 1.0f;
|
||||
heat[px + 1][11] = 1.0f;
|
||||
heat[px - 1][11] = 1.0f;
|
||||
heat[px][12] = 1.0f;
|
||||
heat[px + 1][12] = 1.0f;
|
||||
heat[px - 1][12] = 1.0f;
|
||||
}
|
||||
|
||||
sleep_ms(50);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
float lifetime[53][11];
|
||||
float age[53][11];
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
for(int y = 0; y < 11; y++) {
|
||||
for(int x = 0; x < 53; x++) {
|
||||
lifetime[x][y] = 1.0f + ((rand() % 10) / 100.0f);
|
||||
age[x][y] = ((rand() % 100) / 100.0f) * lifetime[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
while(true) {
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
for(int y = 0; y < 11; y++) {
|
||||
for(int x = 0; x < 53; x++) {
|
||||
if(age[x][y] < lifetime[x][y] * 0.3f) {
|
||||
graphics.set_pen(230, 150, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}else if(age[x][y] < lifetime[x][y] * 0.5f) {
|
||||
float decay = (lifetime[x][y] * 0.5f - age[x][y]) * 5.0f;
|
||||
graphics.set_pen(decay * 230, decay * 150, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
|
||||
if(age[x][y] >= lifetime[x][y]) {
|
||||
age[x][y] = 0.0f;
|
||||
lifetime[x][y] = 1.0f + ((rand() % 10) / 100.0f);
|
||||
}
|
||||
|
||||
age[x][y] += 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// left_channel_bin and right_channel_bin are in audio_samples.cpp
|
||||
extern uint8_t left_channel_bin[];
|
||||
extern uint32_t left_channel_bin_len;
|
||||
extern uint8_t right_channel_bin[];
|
||||
extern uint32_t right_channel_bin_len;
|
||||
|
||||
void gradient(uint8_t r, uint8_t g, uint8_t b) {
|
||||
for(int y = 0; y < 12; y++) {
|
||||
for(int x = 0; x < 53; x++) {
|
||||
// graphics.set_pen((r * x) / 52, (g * x) / 52, (b * x) / 52);
|
||||
graphics.set_pen(x, x, x);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*shader_func_t)(int ms, int x, int y);
|
||||
|
||||
void grid(int ms, int x, int y) {
|
||||
int v = (x + y + (ms / 1000)) % 2 == 0 ? 255 : 0;
|
||||
graphics.set_pen(v, v, v);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
|
||||
void shader_fill(int ms, shader_func_t f) {
|
||||
for(int y = 0; y < 12; y++) {
|
||||
for(int x = 0; x < 53; x++) {
|
||||
f(ms, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void outline_text(std::string text) {
|
||||
uint ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
graphics.set_font("bitmap8");
|
||||
uint8_t v = (sin(ms / 100.0f) + 1.0f) * 127.0f;
|
||||
uint w = graphics.measure_text(text, 1);
|
||||
|
||||
int x = 53 / 2 - w / 2 + 1, y = 2;
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.text(text, Point(x - 1, y - 1), -1, 1);
|
||||
graphics.text(text, Point(x , y - 1), -1, 1);
|
||||
graphics.text(text, Point(x + 1, y - 1), -1, 1);
|
||||
graphics.text(text, Point(x - 1, y ), -1, 1);
|
||||
graphics.text(text, Point(x + 1, y ), -1, 1);
|
||||
graphics.text(text, Point(x - 1, y + 1), -1, 1);
|
||||
graphics.text(text, Point(x , y + 1), -1, 1);
|
||||
graphics.text(text, Point(x + 1, y + 1), -1, 1);
|
||||
|
||||
graphics.set_pen(v, v, v);
|
||||
graphics.text(text, Point(x, y), -1, 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
//galactic_unicorn.set_brightness(0.5f);
|
||||
|
||||
while(true) {
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01f);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01f);
|
||||
}
|
||||
|
||||
uint ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
uint8_t test = (ms / 1000) % 4;
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
switch(test) {
|
||||
case 0: {
|
||||
printf("grid pattern");
|
||||
shader_fill(ms, grid);
|
||||
}break;
|
||||
|
||||
case 1: {
|
||||
printf("green gradient");
|
||||
gradient(0, 255, 0);
|
||||
}break;
|
||||
|
||||
case 2: {
|
||||
printf("blue gradient");
|
||||
gradient(0, 0, 255);
|
||||
}break;
|
||||
|
||||
case 3: {
|
||||
printf("white gradient");
|
||||
gradient(255, 255, 255);
|
||||
}break;
|
||||
}
|
||||
|
||||
printf("%d\n", galactic_unicorn.light());
|
||||
|
||||
std::string text = "";
|
||||
static bool was_a_pressed = false;
|
||||
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_A)) {
|
||||
if(!was_a_pressed) {
|
||||
galactic_unicorn.play_sample(left_channel_bin, left_channel_bin_len);
|
||||
}
|
||||
was_a_pressed = true;
|
||||
}else{
|
||||
was_a_pressed = false;
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_A)) {
|
||||
text = "Button A";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_B)) {
|
||||
text = "Button B";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_C)) {
|
||||
text = "Button C";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_D)) {
|
||||
text = "Button D";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_VOLUME_UP)) {
|
||||
text = "Louder!";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_VOLUME_DOWN)) {
|
||||
text = "quieter";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_BRIGHTNESS_UP)) {
|
||||
text = "Brighter!";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_BRIGHTNESS_DOWN)) {
|
||||
text = "Darker";
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(GalacticUnicorn::SWITCH_SLEEP)) {
|
||||
text = "Zzz... zzz...";
|
||||
}
|
||||
|
||||
outline_text(text);
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(50);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// extra row of pixels for sourcing flames and averaging
|
||||
int width = 53;
|
||||
int height = 15;
|
||||
|
||||
// a buffer that's at least big enough to store 55 x 15 values (to allow for both orientations)
|
||||
float heat[1000] = {0.0f};
|
||||
|
||||
void set(int x, int y, float v) {
|
||||
heat[x + y * width] = v;
|
||||
}
|
||||
|
||||
float get(int x, int y) {
|
||||
/*if(x < 0 || x >= width || y < 0 || y >= height) {
|
||||
return 0.0f;
|
||||
}*/
|
||||
x = x < 0 ? 0 : x;
|
||||
x = x >= width ? width - 1 : x;
|
||||
|
||||
return heat[x + y * width];
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
galactic_unicorn.set_brightness(0.5);
|
||||
|
||||
bool landscape = true;
|
||||
|
||||
while(true) {
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {
|
||||
landscape = true;
|
||||
width = 53;
|
||||
height = 15;
|
||||
memset(heat, 0, sizeof(heat));
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_B)) {
|
||||
landscape = false;
|
||||
width = 11;
|
||||
height = 55;
|
||||
memset(heat, 0, sizeof(heat));
|
||||
}
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
float value = get(x, y);
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
if(value > 0.5f) {
|
||||
graphics.set_pen(255, 255, 180);
|
||||
}else if(value > 0.4f) {
|
||||
graphics.set_pen(220, 160, 0);
|
||||
}else if(value > 0.3f) {
|
||||
graphics.set_pen(180, 30, 0);
|
||||
}else if(value > 0.22f) {
|
||||
graphics.set_pen(20, 20, 20);
|
||||
}
|
||||
|
||||
if(landscape) {
|
||||
graphics.pixel(Point(x, y));
|
||||
}else{
|
||||
graphics.pixel(Point(y, x));
|
||||
}
|
||||
|
||||
// update this pixel by averaging the below pixels
|
||||
float average = (get(x, y) + get(x, y + 2) + get(x, y + 1) + get(x - 1, y + 1) + get(x + 1, y + 1)) / 5.0f;
|
||||
|
||||
// damping factor to ensure flame tapers out towards the top of the displays
|
||||
average *= landscape ? 0.95f : 0.99f;
|
||||
|
||||
// update the heat map with our newly averaged value
|
||||
set(x, y, average);
|
||||
}
|
||||
}
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
// clear the bottom row and then add a new fire seed to it
|
||||
for(int x = 0; x < width; x++) {
|
||||
set(x, height - 1, 0.0f);
|
||||
}
|
||||
|
||||
// add a new random heat source
|
||||
int source_count = landscape ? 5 : 1;
|
||||
for(int c = 0; c < source_count; c++) {
|
||||
int px = (rand() % (width - 4)) + 2;
|
||||
set(px , height - 2, 1.0f);
|
||||
set(px + 1, height - 2, 1.0f);
|
||||
set(px - 1, height - 2, 1.0f);
|
||||
set(px , height - 1, 1.0f);
|
||||
set(px + 1, height - 1, 1.0f);
|
||||
set(px - 1, height - 1, 1.0f);
|
||||
}
|
||||
|
||||
sleep_ms(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
struct blob_t {
|
||||
float x, y;
|
||||
float r;
|
||||
float dx, dy;
|
||||
};
|
||||
|
||||
constexpr int blob_count = 20;
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
galactic_unicorn.set_brightness(0.5);
|
||||
|
||||
// randomise blob start positions, directions, and size
|
||||
std::array<blob_t, blob_count> blobs;
|
||||
for(auto &blob : blobs) {
|
||||
blob.x = rand() % 11;
|
||||
blob.y = rand() % 53;
|
||||
blob.r = ((rand() % 40) / 10.0f) + 5.0f;
|
||||
blob.dx = ((rand() % 2) / 10.0f) - 0.05f;
|
||||
blob.dy = ((rand() % 3) / 10.0f) - 0.1f;
|
||||
}
|
||||
|
||||
float hue = 0.0f;
|
||||
|
||||
while(true) {
|
||||
// allow user to adjust brightness
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
uint start_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
// calculate the influence of each blob on the liquid based
|
||||
// on their distance to each pixel. this causes blobs to
|
||||
// "merge" into each other when we use fixed thresholds to
|
||||
// determine which colour to draw with
|
||||
float liquid[11][53] = {0.0f};
|
||||
for(auto &blob : blobs) {
|
||||
float r_sq = blob.r * blob.r;
|
||||
for(int y = 0; y < 53; y++) {
|
||||
for(int x = 0; x < 11; x++) {
|
||||
float d_sq = (x - blob.x) * (x - blob.x) + (y - blob.y) * (y - blob.y);
|
||||
if(d_sq <= r_sq) {
|
||||
// add this blobs influence to this pixel
|
||||
liquid[x][y] += 1.0f - (d_sq / r_sq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the blob positions
|
||||
for(auto &blob : blobs) {
|
||||
blob.x += blob.dx;
|
||||
blob.y += blob.dy;
|
||||
|
||||
// if we hit the edge then bounce!
|
||||
if(blob.x < 0.0f || blob.x >= 11.0f) {
|
||||
blob.dx *= -1.0f;
|
||||
}
|
||||
if(blob.y < 0.0f || blob.y >= 53.0f) {
|
||||
blob.dy *= -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate the hue
|
||||
hue += 0.001f;
|
||||
|
||||
// calculate dark, medium, and bright shades for rendering the
|
||||
// lava
|
||||
uint8_t dark_r, dark_g, dark_b;
|
||||
from_hsv(hue, 1.0f, 0.3f, dark_r, dark_g, dark_b);
|
||||
uint8_t mid_r, mid_g, mid_b;
|
||||
from_hsv(hue, 1.0f, 0.6f, mid_r, mid_g, mid_b);
|
||||
uint8_t bright_r, bright_g, bright_b;
|
||||
from_hsv(hue, 1.0f, 1.0f, bright_r, bright_g, bright_b);
|
||||
|
||||
// clear the canvas
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
// render the lava
|
||||
for(int y = 0; y < 53; y++) {
|
||||
for(int x = 0; x < 11; x++) {
|
||||
float v = liquid[x][y];
|
||||
|
||||
// select a colour for this pixel based on how much
|
||||
// "blobfluence" there is at this position in the liquid
|
||||
if(v >= 1.5f) {
|
||||
graphics.set_pen(bright_r, bright_g, bright_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}else if(v >= 1.25f) {
|
||||
graphics.set_pen(mid_r, mid_g, mid_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}else if(v >= 1.0f) {
|
||||
graphics.set_pen(dark_r, dark_g, dark_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint end_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
printf("rendering took %dms\n", end_ms - start_ms);
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
std::array<std::string, 11> c64 = {
|
||||
"",
|
||||
" OOOOO OOOOOO OO OOOO OO OO XXXXXXX",
|
||||
" OO OO OO OOOO OO OO OO OO XXXXXXX",
|
||||
" OO OO OO OO OO OO OO OO OO XXXXXXX",
|
||||
" OOOOO OOOO OOOOOO OO OO OOOO XXXXXXX",
|
||||
" OOOO OO OO OO OO OO OO XXXXXXX",
|
||||
" OO OO OO OO OO OO OO OO OO XXXXXXX",
|
||||
" OO OO OOOOOO OO OO OOOO OO OO XXXXXXX",
|
||||
" XXXXXXX"
|
||||
};
|
||||
|
||||
std::array<std::string, 11> spectrum = {
|
||||
"",
|
||||
" O OOOO OOOO OOOOO O O O O XXXXXXXX",
|
||||
" O O O O O O O O O O O X XXXXXX",
|
||||
" O O O O O O O X XXXXXX",
|
||||
" O O O OOOOOO O O X XXXXXX",
|
||||
" O O O O O O O X XXXXXX",
|
||||
" OOOOOO OOOO O O OOOOO X XXXXXX",
|
||||
" X X",
|
||||
" XXXXXXXX"
|
||||
};
|
||||
|
||||
std::array<std::string, 11> bbc_micro = {
|
||||
"",
|
||||
" OOOOO OO OOOO OOO OOOO O ",
|
||||
" O O O O O O O O O O ",
|
||||
" O O O O O O O O ",
|
||||
" OOOOO O O OOOO O O O ",
|
||||
" O O OOOOOO O O O O ",
|
||||
" O O O O O O O O O O ",
|
||||
" OOOOO O O OOOO OOO OOOO O ",
|
||||
" XXXXXXX"
|
||||
};
|
||||
|
||||
|
||||
constexpr uint PROMPT_C64 = 0;
|
||||
constexpr uint PROMPT_SPECTRUM = 1;
|
||||
constexpr uint PROMPT_BBC_MICRO = 2;
|
||||
uint prompt = 0;
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
while(true) {
|
||||
uint time_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
prompt = (time_ms / 3000) % 3;
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
std::array<std::string, 11> image = c64;
|
||||
if(prompt == PROMPT_C64) {
|
||||
image = c64;
|
||||
}else if(prompt == PROMPT_SPECTRUM) {
|
||||
image = spectrum;
|
||||
}else if(prompt == PROMPT_BBC_MICRO) {
|
||||
image = bbc_micro;
|
||||
}
|
||||
|
||||
if(prompt == PROMPT_C64) {
|
||||
graphics.set_pen(20, 20, 120);
|
||||
}else if(prompt == PROMPT_SPECTRUM){
|
||||
graphics.set_pen(180, 150, 150);
|
||||
}else if(prompt == PROMPT_BBC_MICRO){
|
||||
graphics.set_pen(0, 0, 0);
|
||||
}
|
||||
|
||||
graphics.clear();
|
||||
|
||||
if(prompt == PROMPT_C64) {
|
||||
graphics.set_pen(230, 210, 250);
|
||||
}else if(prompt == PROMPT_SPECTRUM){
|
||||
graphics.set_pen(0, 0, 0);
|
||||
}else if(prompt == PROMPT_BBC_MICRO){
|
||||
graphics.set_pen(255, 255, 255);
|
||||
}
|
||||
|
||||
for(size_t y = 0; y < image.size(); y++) {
|
||||
for(size_t x = 0; x < image[y].length(); x++) {
|
||||
// draw the prompt text
|
||||
if(image[y][x] == 'O') {
|
||||
graphics.pixel(Point(x, y + 1));
|
||||
}
|
||||
|
||||
// draw the caret blinking
|
||||
if(image[y][x] == 'X' && (time_ms / 300) % 2) {
|
||||
graphics.pixel(Point(x, y + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,684 @@
|
|||
#pragma once
|
||||
// Copyright(c) 2021 Björn Ottosson
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this softwareand associated documentation files(the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions :
|
||||
// The above copyright noticeand this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
namespace ok_color
|
||||
{
|
||||
|
||||
struct Lab { float L; float a; float b; };
|
||||
struct RGB { float r; float g; float b; };
|
||||
struct HSV { float h; float s; float v; };
|
||||
struct HSL { float h; float s; float l; };
|
||||
struct LC { float L; float C; };
|
||||
|
||||
// Alternative representation of (L_cusp, C_cusp)
|
||||
// Encoded so S = C_cusp/L_cusp and T = C_cusp/(1-L_cusp)
|
||||
// The maximum value for C in the triangle is then found as fmin(S*L, T*(1-L)), for a given L
|
||||
struct ST { float S; float T; };
|
||||
|
||||
constexpr float pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062f;
|
||||
|
||||
float clamp(float x, float min, float max)
|
||||
{
|
||||
if (x < min)
|
||||
return min;
|
||||
if (x > max)
|
||||
return max;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
float sgn(float x)
|
||||
{
|
||||
return (float)(0.f < x) - (float)(x < 0.f);
|
||||
}
|
||||
|
||||
float srgb_transfer_function(float a)
|
||||
{
|
||||
return .0031308f >= a ? 12.92f * a : 1.055f * powf(a, .4166666666666667f) - .055f;
|
||||
}
|
||||
|
||||
float srgb_transfer_function_inv(float a)
|
||||
{
|
||||
return .04045f < a ? powf((a + .055f) / 1.055f, 2.4f) : a / 12.92f;
|
||||
}
|
||||
|
||||
Lab linear_srgb_to_oklab(RGB c)
|
||||
{
|
||||
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
|
||||
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
|
||||
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
|
||||
|
||||
float l_ = cbrtf(l);
|
||||
float m_ = cbrtf(m);
|
||||
float s_ = cbrtf(s);
|
||||
|
||||
return {
|
||||
0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
|
||||
1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
|
||||
0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_,
|
||||
};
|
||||
}
|
||||
|
||||
RGB oklab_to_linear_srgb(Lab c)
|
||||
{
|
||||
float l_ = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
|
||||
float m_ = c.L - 0.1055613458f * c.a - 0.0638541728f * c.b;
|
||||
float s_ = c.L - 0.0894841775f * c.a - 1.2914855480f * c.b;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
return {
|
||||
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
|
||||
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
|
||||
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
|
||||
};
|
||||
}
|
||||
|
||||
// Finds the maximum saturation possible for a given hue that fits in sRGB
|
||||
// Saturation here is defined as S = C/L
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
float compute_max_saturation(float a, float b)
|
||||
{
|
||||
// Max saturation will be when one of r, g or b goes below zero.
|
||||
|
||||
// Select different coefficients depending on which component goes below zero first
|
||||
float k0, k1, k2, k3, k4, wl, wm, ws;
|
||||
|
||||
if (-1.88170328f * a - 0.80936493f * b > 1)
|
||||
{
|
||||
// Red component
|
||||
k0 = +1.19086277f; k1 = +1.76576728f; k2 = +0.59662641f; k3 = +0.75515197f; k4 = +0.56771245f;
|
||||
wl = +4.0767416621f; wm = -3.3077115913f; ws = +0.2309699292f;
|
||||
}
|
||||
else if (1.81444104f * a - 1.19445276f * b > 1)
|
||||
{
|
||||
// Green component
|
||||
k0 = +0.73956515f; k1 = -0.45954404f; k2 = +0.08285427f; k3 = +0.12541070f; k4 = +0.14503204f;
|
||||
wl = -1.2684380046f; wm = +2.6097574011f; ws = -0.3413193965f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blue component
|
||||
k0 = +1.35733652f; k1 = -0.00915799f; k2 = -1.15130210f; k3 = -0.50559606f; k4 = +0.00692167f;
|
||||
wl = -0.0041960863f; wm = -0.7034186147f; ws = +1.7076147010f;
|
||||
}
|
||||
|
||||
// Approximate max saturation using a polynomial:
|
||||
float S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
|
||||
|
||||
// Do one step Halley's method to get closer
|
||||
// this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite
|
||||
// this should be sufficient for most applications, otherwise do two/three steps
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
{
|
||||
float l_ = 1.f + S * k_l;
|
||||
float m_ = 1.f + S * k_m;
|
||||
float s_ = 1.f + S * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float l_dS = 3.f * k_l * l_ * l_;
|
||||
float m_dS = 3.f * k_m * m_ * m_;
|
||||
float s_dS = 3.f * k_s * s_ * s_;
|
||||
|
||||
float l_dS2 = 6.f * k_l * k_l * l_;
|
||||
float m_dS2 = 6.f * k_m * k_m * m_;
|
||||
float s_dS2 = 6.f * k_s * k_s * s_;
|
||||
|
||||
float f = wl * l + wm * m + ws * s;
|
||||
float f1 = wl * l_dS + wm * m_dS + ws * s_dS;
|
||||
float f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
|
||||
|
||||
S = S - f * f1 / (f1 * f1 - 0.5f * f * f2);
|
||||
}
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
// finds L_cusp and C_cusp for a given hue
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
LC find_cusp(float a, float b)
|
||||
{
|
||||
// First, find the maximum saturation (saturation S = C/L)
|
||||
float S_cusp = compute_max_saturation(a, b);
|
||||
|
||||
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
|
||||
RGB rgb_at_max = oklab_to_linear_srgb({ 1, S_cusp * a, S_cusp * b });
|
||||
float L_cusp = cbrtf(1.f / fmax(fmax(rgb_at_max.r, rgb_at_max.g), rgb_at_max.b));
|
||||
float C_cusp = L_cusp * S_cusp;
|
||||
|
||||
return { L_cusp , C_cusp };
|
||||
}
|
||||
|
||||
// Finds intersection of the line defined by
|
||||
// L = L0 * (1 - t) + t * L1;
|
||||
// C = t * C1;
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
float find_gamut_intersection(float a, float b, float L1, float C1, float L0, LC cusp)
|
||||
{
|
||||
// Find the intersection for upper and lower half seprately
|
||||
float t;
|
||||
if (((L1 - L0) * cusp.C - (cusp.L - L0) * C1) <= 0.f)
|
||||
{
|
||||
// Lower half
|
||||
|
||||
t = cusp.C * L0 / (C1 * cusp.L + cusp.C * (L0 - L1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper half
|
||||
|
||||
// First intersect with triangle
|
||||
t = cusp.C * (L0 - 1.f) / (C1 * (cusp.L - 1.f) + cusp.C * (L0 - L1));
|
||||
|
||||
// Then one step Halley's method
|
||||
{
|
||||
float dL = L1 - L0;
|
||||
float dC = C1;
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
float l_dt = dL + dC * k_l;
|
||||
float m_dt = dL + dC * k_m;
|
||||
float s_dt = dL + dC * k_s;
|
||||
|
||||
|
||||
// If higher accuracy is required, 2 or 3 iterations of the following block can be used:
|
||||
{
|
||||
float L = L0 * (1.f - t) + t * L1;
|
||||
float C = t * C1;
|
||||
|
||||
float l_ = L + C * k_l;
|
||||
float m_ = L + C * k_m;
|
||||
float s_ = L + C * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float ldt = 3 * l_dt * l_ * l_;
|
||||
float mdt = 3 * m_dt * m_ * m_;
|
||||
float sdt = 3 * s_dt * s_ * s_;
|
||||
|
||||
float ldt2 = 6 * l_dt * l_dt * l_;
|
||||
float mdt2 = 6 * m_dt * m_dt * m_;
|
||||
float sdt2 = 6 * s_dt * s_dt * s_;
|
||||
|
||||
float r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s - 1;
|
||||
float r1 = 4.0767416621f * ldt - 3.3077115913f * mdt + 0.2309699292f * sdt;
|
||||
float r2 = 4.0767416621f * ldt2 - 3.3077115913f * mdt2 + 0.2309699292f * sdt2;
|
||||
|
||||
float u_r = r1 / (r1 * r1 - 0.5f * r * r2);
|
||||
float t_r = -r * u_r;
|
||||
|
||||
float g = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s - 1;
|
||||
float g1 = -1.2684380046f * ldt + 2.6097574011f * mdt - 0.3413193965f * sdt;
|
||||
float g2 = -1.2684380046f * ldt2 + 2.6097574011f * mdt2 - 0.3413193965f * sdt2;
|
||||
|
||||
float u_g = g1 / (g1 * g1 - 0.5f * g * g2);
|
||||
float t_g = -g * u_g;
|
||||
|
||||
float b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s - 1;
|
||||
float b1 = -0.0041960863f * ldt - 0.7034186147f * mdt + 1.7076147010f * sdt;
|
||||
float b2 = -0.0041960863f * ldt2 - 0.7034186147f * mdt2 + 1.7076147010f * sdt2;
|
||||
|
||||
float u_b = b1 / (b1 * b1 - 0.5f * b * b2);
|
||||
float t_b = -b * u_b;
|
||||
|
||||
t_r = u_r >= 0.f ? t_r : FLT_MAX;
|
||||
t_g = u_g >= 0.f ? t_g : FLT_MAX;
|
||||
t_b = u_b >= 0.f ? t_b : FLT_MAX;
|
||||
|
||||
t += fmin(t_r, fmin(t_g, t_b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
float find_gamut_intersection(float a, float b, float L1, float C1, float L0)
|
||||
{
|
||||
// Find the cusp of the gamut triangle
|
||||
LC cusp = find_cusp(a, b);
|
||||
|
||||
return find_gamut_intersection(a, b, L1, C1, L0, cusp);
|
||||
}
|
||||
|
||||
RGB gamut_clip_preserve_chroma(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L0 = clamp(L, 0, 1);
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
RGB gamut_clip_project_to_0_5(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L0 = 0.5;
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
RGB gamut_clip_project_to_L_cusp(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float L0 = cusp.L;
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
RGB gamut_clip_adaptive_L0_0_5(RGB rgb, float alpha = 0.05f)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float Ld = L - 0.5f;
|
||||
float e1 = 0.5f + fabs(Ld) + alpha * C;
|
||||
float L0 = 0.5f * (1.f + sgn(Ld) * (e1 - sqrtf(e1 * e1 - 2.f * fabs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
RGB gamut_clip_adaptive_L0_L_cusp(RGB rgb, float alpha = 0.05f)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float Ld = L - cusp.L;
|
||||
float k = 2.f * (Ld > 0 ? 1.f - cusp.L : cusp.L);
|
||||
|
||||
float e1 = 0.5f * k + fabs(Ld) + alpha * C / k;
|
||||
float L0 = cusp.L + 0.5f * (sgn(Ld) * (e1 - sqrtf(e1 * e1 - 2.f * k * fabs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
float toe(float x)
|
||||
{
|
||||
constexpr float k_1 = 0.206f;
|
||||
constexpr float k_2 = 0.03f;
|
||||
constexpr float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return 0.5f * (k_3 * x - k_1 + sqrtf((k_3 * x - k_1) * (k_3 * x - k_1) + 4 * k_2 * k_3 * x));
|
||||
}
|
||||
|
||||
float toe_inv(float x)
|
||||
{
|
||||
constexpr float k_1 = 0.206f;
|
||||
constexpr float k_2 = 0.03f;
|
||||
constexpr float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return (x * x + k_1 * x) / (k_3 * (x + k_2));
|
||||
}
|
||||
|
||||
ST to_ST(LC cusp)
|
||||
{
|
||||
float L = cusp.L;
|
||||
float C = cusp.C;
|
||||
return { C / L, C / (1 - L) };
|
||||
}
|
||||
|
||||
// Returns a smooth approximation of the location of the cusp
|
||||
// This polynomial was created by an optimization process
|
||||
// It has been designed so that S_mid < S_max and T_mid < T_max
|
||||
ST get_ST_mid(float a_, float b_)
|
||||
{
|
||||
float S = 0.11516993f + 1.f / (
|
||||
+7.44778970f + 4.15901240f * b_
|
||||
+ a_ * (-2.19557347f + 1.75198401f * b_
|
||||
+ a_ * (-2.13704948f - 10.02301043f * b_
|
||||
+ a_ * (-4.24894561f + 5.38770819f * b_ + 4.69891013f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
float T = 0.11239642f + 1.f / (
|
||||
+1.61320320f - 0.68124379f * b_
|
||||
+ a_ * (+0.40370612f + 0.90148123f * b_
|
||||
+ a_ * (-0.27087943f + 0.61223990f * b_
|
||||
+ a_ * (+0.00299215f - 0.45399568f * b_ - 0.14661872f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
return { S, T };
|
||||
}
|
||||
|
||||
struct Cs { float C_0; float C_mid; float C_max; };
|
||||
Cs get_Cs(float L, float a_, float b_)
|
||||
{
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float C_max = find_gamut_intersection(a_, b_, L, 1, L, cusp);
|
||||
ST ST_max = to_ST(cusp);
|
||||
|
||||
// Scale factor to compensate for the curved part of gamut shape:
|
||||
float k = C_max / fmin((L * ST_max.S), (1 - L) * ST_max.T);
|
||||
|
||||
float C_mid;
|
||||
{
|
||||
ST ST_mid = get_ST_mid(a_, b_);
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
float C_a = L * ST_mid.S;
|
||||
float C_b = (1.f - L) * ST_mid.T;
|
||||
C_mid = 0.9f * k * sqrtf(sqrtf(1.f / (1.f / (C_a * C_a * C_a * C_a) + 1.f / (C_b * C_b * C_b * C_b))));
|
||||
}
|
||||
|
||||
float C_0;
|
||||
{
|
||||
// for C_0, the shape is independent of hue, so ST are constant. Values picked to roughly be the average values of ST.
|
||||
float C_a = L * 0.4f;
|
||||
float C_b = (1.f - L) * 0.8f;
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
C_0 = sqrtf(1.f / (1.f / (C_a * C_a) + 1.f / (C_b * C_b)));
|
||||
}
|
||||
|
||||
return { C_0, C_mid, C_max };
|
||||
}
|
||||
|
||||
RGB okhsl_to_srgb(HSL hsl)
|
||||
{
|
||||
float h = hsl.h;
|
||||
float s = hsl.s;
|
||||
float l = hsl.l;
|
||||
|
||||
if (l == 1.0f)
|
||||
{
|
||||
return { 1.f, 1.f, 1.f };
|
||||
}
|
||||
|
||||
else if (l == 0.f)
|
||||
{
|
||||
return { 0.f, 0.f, 0.f };
|
||||
}
|
||||
|
||||
float a_ = cosf(2.f * pi * h);
|
||||
float b_ = sinf(2.f * pi * h);
|
||||
float L = toe_inv(l);
|
||||
|
||||
Cs cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.C_0;
|
||||
float C_mid = cs.C_mid;
|
||||
float C_max = cs.C_max;
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float C, t, k_0, k_1, k_2;
|
||||
|
||||
if (s < mid)
|
||||
{
|
||||
t = mid_inv * s;
|
||||
|
||||
k_1 = mid * C_0;
|
||||
k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
C = t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (s - mid)/ (1 - mid);
|
||||
|
||||
k_0 = C_mid;
|
||||
k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
C = k_0 + t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
|
||||
RGB rgb = oklab_to_linear_srgb({ L, C * a_, C * b_ });
|
||||
return {
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b),
|
||||
};
|
||||
}
|
||||
|
||||
HSL srgb_to_okhsl(RGB rgb)
|
||||
{
|
||||
Lab lab = linear_srgb_to_oklab({
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
});
|
||||
|
||||
float C = sqrtf(lab.a * lab.a + lab.b * lab.b);
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L = lab.L;
|
||||
float h = 0.5f + 0.5f * atan2f(-lab.b, -lab.a) / pi;
|
||||
|
||||
Cs cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.C_0;
|
||||
float C_mid = cs.C_mid;
|
||||
float C_max = cs.C_max;
|
||||
|
||||
// Inverse of the interpolation in okhsl_to_srgb:
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float s;
|
||||
if (C < C_mid)
|
||||
{
|
||||
float k_1 = mid * C_0;
|
||||
float k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
float t = C / (k_1 + k_2 * C);
|
||||
s = t * mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
float k_0 = C_mid;
|
||||
float k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
float k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
float t = (C - k_0) / (k_1 + k_2 * (C - k_0));
|
||||
s = mid + (1.f - mid) * t;
|
||||
}
|
||||
|
||||
float l = toe(L);
|
||||
return { h, s, l };
|
||||
}
|
||||
|
||||
|
||||
RGB okhsv_to_srgb(HSV hsv)
|
||||
{
|
||||
float h = hsv.h;
|
||||
float s = hsv.s;
|
||||
float v = hsv.v;
|
||||
|
||||
float a_ = cosf(2.f * pi * h);
|
||||
float b_ = sinf(2.f * pi * h);
|
||||
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
ST ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.S;
|
||||
float T_max = ST_max.T;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1 - S_0 / S_max;
|
||||
|
||||
// first we compute L and V as if the gamut is a perfect triangle:
|
||||
|
||||
// L, C when v==1:
|
||||
float L_v = 1 - s * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
float C_v = s * T_max * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
|
||||
float L = v * L_v;
|
||||
float C = v * C_v;
|
||||
|
||||
// then we compensate for both toe and the curved top part of the triangle:
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
float L_new = toe_inv(L);
|
||||
C = C * L_new / L;
|
||||
L = L_new;
|
||||
|
||||
RGB rgb_scale = oklab_to_linear_srgb({ L_vt, a_ * C_vt, b_ * C_vt });
|
||||
float scale_L = cbrtf(1.f / fmax(fmax(rgb_scale.r, rgb_scale.g), fmax(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L * scale_L;
|
||||
C = C * scale_L;
|
||||
|
||||
RGB rgb = oklab_to_linear_srgb({ L, C * a_, C * b_ });
|
||||
return {
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b),
|
||||
};
|
||||
}
|
||||
|
||||
HSV srgb_to_okhsv(RGB rgb)
|
||||
{
|
||||
Lab lab = linear_srgb_to_oklab({
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
});
|
||||
|
||||
float C = sqrtf(lab.a * lab.a + lab.b * lab.b);
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L = lab.L;
|
||||
float h = 0.5f + 0.5f * atan2f(-lab.b, -lab.a) / pi;
|
||||
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
ST ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.S;
|
||||
float T_max = ST_max.T;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1 - S_0 / S_max;
|
||||
|
||||
// first we find L_v, C_v, L_vt and C_vt
|
||||
|
||||
float t = T_max / (C + L * T_max);
|
||||
float L_v = t * L;
|
||||
float C_v = t * C;
|
||||
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
// we can then use these to invert the step that compensates for the toe and the curved top part of the triangle:
|
||||
RGB rgb_scale = oklab_to_linear_srgb({ L_vt, a_ * C_vt, b_ * C_vt });
|
||||
float scale_L = cbrtf(1.f / fmax(fmax(rgb_scale.r, rgb_scale.g), fmax(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L / scale_L;
|
||||
C = C / scale_L;
|
||||
|
||||
C = C * toe(L) / L;
|
||||
L = toe(L);
|
||||
|
||||
// we can now compute v and s:
|
||||
|
||||
float v = L / L_v;
|
||||
float s = (S_0 + T_max) * C_v / ((T_max * S_0) + T_max * k * C_v);
|
||||
|
||||
return { h, s, v };
|
||||
}
|
||||
|
||||
} // namespace ok_color
|
|
@ -0,0 +1,200 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) {
|
||||
int w = graphics.measure_text(t, s);
|
||||
p.x += (53 / 2) - (w / 2);
|
||||
p.y += (11 / 2);
|
||||
graphics.text(t, Point(p.x, p.y), -1, s, a);
|
||||
//graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
|
||||
//graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
|
||||
//graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
|
||||
}
|
||||
|
||||
struct star_t {
|
||||
float dx, dy, x, y, a;
|
||||
|
||||
uint8_t brightness() {
|
||||
int b = a / 5;
|
||||
return b > 15 ? 15 : b;
|
||||
}
|
||||
};
|
||||
|
||||
void init_star(star_t &s) {
|
||||
s.x = ((rand() % 100) / 5.0f) - 10.0f;
|
||||
s.y = ((rand() % 100) / 10.0f) - 5.0f;
|
||||
|
||||
s.dx = s.x / 10.0f;
|
||||
s.dy = s.y / 10.0f;
|
||||
s.a = 0;
|
||||
}
|
||||
|
||||
void step_star(star_t &s) {
|
||||
s.x += s.dx;
|
||||
s.y += s.dy;
|
||||
s.a++;
|
||||
|
||||
if(s.a > 100) {
|
||||
init_star(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
uint8_t hue_map[53][3];
|
||||
for(int i = 0; i < 53; i++) {
|
||||
from_hsv(i / 53.0f, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
|
||||
}
|
||||
|
||||
star_t stars[100];
|
||||
for(int i = 0; i < 100; i++) {
|
||||
init_star(stars[i]);
|
||||
stars[i].a = i;
|
||||
}
|
||||
|
||||
gpio_set_function(28, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(28, GPIO_OUT);
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
gpio_put(28, !gpio_get(28));
|
||||
sleep_ms(100);
|
||||
}
|
||||
sleep_ms(1000);
|
||||
|
||||
gpio_put(28,true);
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
/*
|
||||
bool a_pressed = false;
|
||||
bool b_pressed = false;
|
||||
bool x_pressed = false;
|
||||
bool y_pressed = false;
|
||||
*/
|
||||
graphics.set_font("bitmap8");
|
||||
|
||||
|
||||
|
||||
float i = 0;
|
||||
|
||||
float hue_offset = 0.0f;
|
||||
|
||||
bool animate = true;
|
||||
|
||||
float stripe_width = 3.0f;
|
||||
float speed = 1.0f;
|
||||
float curve = 0.0f;
|
||||
|
||||
while(true) {
|
||||
|
||||
if(animate) {
|
||||
i += speed;
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_UP)) {
|
||||
curve += 0.05;
|
||||
if(hue_offset > 1.0f) hue_offset = 1.0f;
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_DOWN)) {
|
||||
curve -= 0.05;
|
||||
if(hue_offset < 0.0f) hue_offset = 0.0f;
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_SLEEP)) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {
|
||||
speed += 0.05f;
|
||||
speed = speed >= 10.0f ? 10.0f : speed;
|
||||
animate = true;
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_B)) {
|
||||
speed -= 0.05f;
|
||||
speed = speed <= 0.0f ? 0.0f : speed;
|
||||
animate = true;
|
||||
}
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_C)) {
|
||||
stripe_width += 0.05f;
|
||||
stripe_width = stripe_width >= 10.0f ? 10.0f : stripe_width;
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_D)) {
|
||||
stripe_width -= 0.05f;
|
||||
stripe_width = stripe_width <= 1.0f ? 1.0f : stripe_width;
|
||||
}
|
||||
|
||||
/*
|
||||
graphics.set_pen(255, 255, 255);
|
||||
float s = 0.65f;//0.65f + (sin(i / 25.0f) * 0.15f);
|
||||
float a = 1.0f;// (sin(i / 25.0f) * 100.0f);
|
||||
float x = (sin(i / 74.0f) * 80.0f) * s;
|
||||
float y = (cos(i / 43.0f) * 6.0f) * s;
|
||||
text("Chester smells!", Point(x, y - 3), s, a);
|
||||
*/
|
||||
|
||||
for(int x = 0; x < 53; x++) {
|
||||
for(int y = 0; y < 11; y++) {
|
||||
int v = ((sin((x + y) / stripe_width + (sin((y * 3.1415927f * 2.0f) / 11.0f) * curve) + i / 15.0f) + 1.5f) / 2.5f) * 255.0f;
|
||||
|
||||
uint8_t r = (hue_map[x][0] * v) / 256;
|
||||
uint8_t g = (hue_map[x][1] * v) / 256;
|
||||
uint8_t b = (hue_map[x][2] * v) / 256;
|
||||
|
||||
graphics.set_pen(r, g, b);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
}
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
printf("%d\n", galactic_unicorn.light());
|
||||
sleep_ms(20);
|
||||
}
|
||||
|
||||
|
||||
|
||||
printf("done\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) {
|
||||
int w = graphics.measure_text(t, s);
|
||||
p.x += (53 / 2) - (w / 2);
|
||||
p.y += (11 / 2);
|
||||
graphics.text(t, Point(p.x, p.y), -1, s, a);
|
||||
graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
|
||||
graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
|
||||
graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
uint8_t hue_map[53][3];
|
||||
for(int i = 0; i < 53; i++) {
|
||||
from_hsv(i / 53.0f, 1.0f, 0.1f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
|
||||
}
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
graphics.set_font("sans");
|
||||
uint i = 0;
|
||||
|
||||
while(true) {
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
|
||||
i++;
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
float s = 0.8f;//0.65f + (sin(i / 25.0f) * 0.15f);
|
||||
float a = 1.0f;// (sin(i / 25.0f) * 100.0f);
|
||||
|
||||
float x = (sin((i) / 50.0f) * 90.0f);
|
||||
float y = (cos((i) / 40.0f) * 5.0f);
|
||||
graphics.set_pen(255, 255, 255);
|
||||
text("Galactic Unicorn", Point(x, y), s, a);
|
||||
|
||||
uint8_t *p = (uint8_t *)graphics.frame_buffer;
|
||||
for(size_t i = 0; i < 53 * 11; i++) {
|
||||
int x = i % 53;
|
||||
int y = i / 53;
|
||||
uint r = *p++;
|
||||
uint g = *p++;
|
||||
uint b = *p++;
|
||||
p++;
|
||||
|
||||
if(r > 0) {
|
||||
r = hue_map[x][0];
|
||||
g = hue_map[x][1];
|
||||
b = hue_map[x][2];
|
||||
}
|
||||
|
||||
graphics.set_pen(r, g, b);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
printf("done\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "galactic_unicorn.hpp"
|
||||
#include "okcolor.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
|
||||
GalacticUnicorn galactic_unicorn;
|
||||
|
||||
std::string message = "Pirate. Monkey. Robot. Ninja.";
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
galactic_unicorn.init();
|
||||
|
||||
float scroll = -53.0f;
|
||||
|
||||
while(true) {
|
||||
//uint time_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
galactic_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
galactic_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
int width = graphics.measure_text(message, 1);
|
||||
scroll += 0.25f;
|
||||
|
||||
if(scroll > width) {
|
||||
scroll = -53.0f;
|
||||
}
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
ok_color::HSL hsl{scroll / 100.0f, 1.0f, 0.5f};
|
||||
ok_color::RGB rgb = ok_color::okhsl_to_srgb(hsl);
|
||||
graphics.set_pen(rgb.r * 255, rgb.g * 255, rgb.b * 255);
|
||||
graphics.text(message, Point(0 - scroll, 5), -1, 0.55);
|
||||
/*graphics.text(message, Point(0 - scroll + 1, 5), -1, 0.5);
|
||||
graphics.text(message, Point(0 - scroll, 5 + 1), -1, 0.5);
|
||||
graphics.text(message, Point(0 - scroll + 1, 5 + 1), -1, 0.5);*/
|
||||
/*
|
||||
for(int x = 0; x < 53; x++) {
|
||||
for(int y = 0; y < 2; y++) {
|
||||
float b = sin((x - y * 3 + int(scroll) + 100) / 3.0f);
|
||||
graphics.set_pen(180 * b, 150 * b, 0);
|
||||
graphics.pixel(Point(52 - x, y));
|
||||
graphics.pixel(Point(52 - x, y + 9));
|
||||
}
|
||||
}*/
|
||||
|
||||
galactic_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -36,3 +36,4 @@ add_subdirectory(inventor2040w)
|
|||
add_subdirectory(adcfft)
|
||||
add_subdirectory(jpegdec)
|
||||
add_subdirectory(inky_frame)
|
||||
add_subdirectory(galactic_unicorn)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(galactic_unicorn.cmake)
|
|
@ -0,0 +1,114 @@
|
|||
# Galactic Unicorn <!-- omit in toc -->
|
||||
|
||||
Galactic Unicorn offers 53x11 7x17 bright RGB LEDs driven by Pico W's PIO in addition to a 1W amplifier and speaker.
|
||||
|
||||
Galactic Unicorn uses SM0 of PIO0.
|
||||
|
||||
TODO: Update documentation
|
||||
|
||||
We've included helper functions to handle every aspect of drawing to the display and interfacing with the buttons. See the [function reference](#function-reference) for details.
|
||||
|
||||
- [Example Program](#example-program)
|
||||
- [Reference](#reference)
|
||||
- [Constants](#constants)
|
||||
- [Buttons](#buttons)
|
||||
- [WIDTH / HEIGHT](#width--height)
|
||||
- [Functions](#functions)
|
||||
- [init](#init)
|
||||
- [set_pixel](#set_pixel)
|
||||
- [is_pressed](#is_pressed)
|
||||
- [Examples](#examples)
|
||||
|
||||
## Example Program
|
||||
|
||||
The following example sets up Pico Unicorn, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is presse
|
||||
|
||||
```c++
|
||||
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Constants
|
||||
|
||||
#### Buttons
|
||||
|
||||
The four buttons, A, B, X and Y have correponding constants set to their respective GPIO pins. For example:
|
||||
|
||||
```c++
|
||||
bool a_is_pressed = pico_unicorn.is_pressed(pico_unicorn.A);
|
||||
```
|
||||
|
||||
#### WIDTH / HEIGHT
|
||||
|
||||
The width and height of Pico Unicorn are available in constants `WIDTH` and `HEIGHT`.
|
||||
|
||||
For example:
|
||||
|
||||
```c++
|
||||
int num_pixels = pico_unicorn.WIDTH * pico_unicorn.HEIGHT;
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
#### init
|
||||
|
||||
Sets up Pico Unicorn. `init` must be called before any other functions since it configures the PIO and require GPIO inputs. Just call `init()` like so:
|
||||
|
||||
```c++
|
||||
PicoUnicorn pico_unicorn;
|
||||
pico_unicorn.init();
|
||||
```
|
||||
|
||||
#### set_pixel
|
||||
|
||||
```c++
|
||||
void set_pixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_pixel(uint8_t x, uint8_t y, uint8_t v);
|
||||
```
|
||||
|
||||
Sets an RGB LED on Pico Unicorn with an RGB triplet:
|
||||
|
||||
```c++
|
||||
pico_unicorn.set_pixel(x, y, r, g, b);
|
||||
```
|
||||
|
||||
Uses hardware PWM to drive the LED. Values are automatically gamma-corrected to provide smooth brightness transitions and low values may map as "off."
|
||||
|
||||
Alternatively you can use:
|
||||
|
||||
```c++
|
||||
pico_unicorn.set_pixel(x, y, v);
|
||||
```
|
||||
|
||||
Which sets the R, G and B elements of the pixel to the same value- lighting it up white at your chosen intensity.
|
||||
|
||||
#### is_pressed
|
||||
|
||||
```c++
|
||||
bool is_pressed(uint8_t button);
|
||||
```
|
||||
|
||||
Reads the GPIO pin connected to one of Pico Unicorn's buttons, returning a `bool` - `true` if it's pressed and `false` if it is released.
|
||||
|
||||
```c++
|
||||
pico_unicorn.is_pressed(button);
|
||||
```
|
||||
|
||||
The button vaule should be a `uint8_t` denoting a pin, and constants `A`, `B`, `X` and `Y` are supplied to make it easier. e:
|
||||
|
||||
```c++
|
||||
bool is_a_button_pressed = pico_unicorn.is_pressed(PicoUnicorn::A)
|
||||
```
|
||||
|
||||
# Examples
|
||||
|
||||
## Game of Life
|
||||
|
||||
## Retro Super-computer
|
||||
|
||||
Random LEDs blink on and off mimicing the look of a movie super computer doing it's work in the eighties.
|
||||
|
||||
## Nostalgia Terminal
|
||||
|
||||
A collection of copies of classic terminal styles including C64, MS-DOS, Spectrum, and more.
|
|
@ -0,0 +1,63 @@
|
|||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
; Transmit a mono or stereo I2S audio stream as stereo
|
||||
; This is 16 bits per sample; can be altered by modifying the "set" params,
|
||||
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
|
||||
;
|
||||
; Autopull must be enabled, with threshold set to 32.
|
||||
; Since I2S is MSB-first, shift direction should be to left.
|
||||
; Hence the format of the FIFO word is:
|
||||
;
|
||||
; | 31 : 16 | 15 : 0 |
|
||||
; | sample ws=0 | sample ws=1 |
|
||||
;
|
||||
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
|
||||
; Fractional divider will probably be needed to get correct bit clock period,
|
||||
; but for common syslck freqs this should still give a constant word select period.
|
||||
;
|
||||
; One output pin is used for the data output.
|
||||
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
|
||||
|
||||
; Send 16 bit words to the PIO for mono, 32 bit words for stereo
|
||||
|
||||
.program audio_i2s
|
||||
.side_set 2
|
||||
|
||||
; /--- LRCLK
|
||||
; |/-- BCLK
|
||||
bitloop1: ; ||
|
||||
out pins, 1 side 0b10
|
||||
jmp x-- bitloop1 side 0b11
|
||||
out pins, 1 side 0b00
|
||||
set x, 14 side 0b01
|
||||
|
||||
bitloop0:
|
||||
out pins, 1 side 0b00
|
||||
jmp x-- bitloop0 side 0b01
|
||||
out pins, 1 side 0b10
|
||||
public entry_point:
|
||||
set x, 14 side 0b11
|
||||
|
||||
% c-sdk {
|
||||
|
||||
static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
|
||||
pio_sm_config sm_config = audio_i2s_program_get_default_config(offset);
|
||||
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, 32);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, pin_mask); // clear pins
|
||||
|
||||
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point));
|
||||
}
|
||||
|
||||
%}
|
|
@ -0,0 +1,15 @@
|
|||
add_library(galactic_unicorn INTERFACE)
|
||||
|
||||
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.pio)
|
||||
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio)
|
||||
|
||||
|
||||
target_sources(galactic_unicorn INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../pico_synth/pico_synth.cpp
|
||||
)
|
||||
|
||||
target_include_directories(galactic_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(galactic_unicorn INTERFACE pico_stdlib pico_graphics hardware_adc hardware_pio hardware_dma)
|
|
@ -0,0 +1,569 @@
|
|||
#include <math.h>
|
||||
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/adc.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
#include "galactic_unicorn.pio.h"
|
||||
#include "audio_i2s.pio.h"
|
||||
|
||||
#include "galactic_unicorn.hpp"
|
||||
|
||||
// pixel data is stored as a stream of bits delivered in the
|
||||
// order the PIO needs to manage the shift registers, row
|
||||
// selects, delays, and latching/blanking
|
||||
//
|
||||
// the pins used are:
|
||||
//
|
||||
// - 13: column clock (sideset)
|
||||
// - 14: column data (out base)
|
||||
// - 15: column latch
|
||||
// - 16: column blank
|
||||
// - 17: row select bit 0
|
||||
// - 18: row select bit 1
|
||||
// - 19: row select bit 2
|
||||
// - 20: row select bit 3
|
||||
//
|
||||
// the framebuffer data is structured like this:
|
||||
//
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
// 0: 00110110 // row pixel count (minus one)
|
||||
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
// 56: xxxxrrrr // row select bits
|
||||
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
//
|
||||
// .. and back to the start
|
||||
|
||||
static uint16_t r_gamma_lut[256] = {0};
|
||||
static uint16_t g_gamma_lut[256] = {0};
|
||||
static uint16_t b_gamma_lut[256] = {0};
|
||||
|
||||
static uint32_t dma_channel;
|
||||
static uint32_t dma_ctrl_channel;
|
||||
static uint32_t audio_dma_channel;
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
GalacticUnicorn* GalacticUnicorn::unicorn = nullptr;
|
||||
PIO GalacticUnicorn::bitstream_pio = pio0;
|
||||
uint GalacticUnicorn::bitstream_sm = 0;
|
||||
uint GalacticUnicorn::bitstream_sm_offset = 0;
|
||||
PIO GalacticUnicorn::audio_pio = pio0;
|
||||
uint GalacticUnicorn::audio_sm = 0;
|
||||
uint GalacticUnicorn::audio_sm_offset = 0;
|
||||
|
||||
// once the dma transfer of the scanline is complete we move to the
|
||||
// next scanline (or quit if we're finished)
|
||||
void __isr GalacticUnicorn::dma_complete() {
|
||||
if(unicorn != nullptr && dma_channel_get_irq0_status(audio_dma_channel)) {
|
||||
unicorn->next_audio_sequence();
|
||||
}
|
||||
}
|
||||
|
||||
GalacticUnicorn::~GalacticUnicorn() {
|
||||
if(unicorn == this) {
|
||||
partial_teardown();
|
||||
|
||||
dma_channel_unclaim(dma_ctrl_channel); // This works now the teardown behaves correctly
|
||||
dma_channel_unclaim(dma_channel); // This works now the teardown behaves correctly
|
||||
pio_sm_unclaim(bitstream_pio, bitstream_sm);
|
||||
pio_remove_program(bitstream_pio, &galactic_unicorn_program, bitstream_sm_offset);
|
||||
|
||||
dma_channel_unclaim(audio_dma_channel); // This works now the teardown behaves correctly
|
||||
pio_sm_unclaim(audio_pio, audio_sm);
|
||||
pio_remove_program(audio_pio, &audio_i2s_program, audio_sm_offset);
|
||||
irq_remove_handler(DMA_IRQ_0, dma_complete);
|
||||
|
||||
unicorn = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::partial_teardown() {
|
||||
// Stop the bitstream SM
|
||||
pio_sm_set_enabled(bitstream_pio, bitstream_sm, false);
|
||||
|
||||
// Make sure the display is off and switch it to an invisible row, to be safe
|
||||
const uint pins_to_set = 1 << COLUMN_BLANK | 0b1111 << ROW_BIT_0;
|
||||
pio_sm_set_pins_with_mask(bitstream_pio, bitstream_sm, pins_to_set, pins_to_set);
|
||||
|
||||
|
||||
dma_hw->ch[dma_ctrl_channel].al1_ctrl = (dma_hw->ch[dma_ctrl_channel].al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_ctrl_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
|
||||
dma_hw->ch[dma_channel].al1_ctrl = (dma_hw->ch[dma_channel].al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
|
||||
// Abort any in-progress DMA transfer
|
||||
dma_safe_abort(dma_ctrl_channel);
|
||||
//dma_channel_abort(dma_ctrl_channel);
|
||||
//dma_channel_abort(dma_channel);
|
||||
dma_safe_abort(dma_channel);
|
||||
|
||||
|
||||
// Stop the audio SM
|
||||
pio_sm_set_enabled(audio_pio, audio_sm, false);
|
||||
|
||||
// Reset the I2S pins to avoid popping when audio is suddenly stopped
|
||||
const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
|
||||
pio_sm_set_pins_with_mask(audio_pio, audio_sm, 0, pins_to_clear);
|
||||
|
||||
// Abort any in-progress DMA transfer
|
||||
dma_safe_abort(audio_dma_channel);
|
||||
}
|
||||
|
||||
uint16_t GalacticUnicorn::light() {
|
||||
adc_select_input(2);
|
||||
return adc_read();
|
||||
}
|
||||
|
||||
void GalacticUnicorn::init() {
|
||||
|
||||
if(unicorn != nullptr) {
|
||||
// Tear down the old GU instance's hardware resources
|
||||
partial_teardown();
|
||||
}
|
||||
|
||||
|
||||
// create 14-bit gamma luts
|
||||
for(uint16_t v = 0; v < 256; v++) {
|
||||
// gamma correct the provided 0-255 brightness value onto a
|
||||
// 0-65535 range for the pwm counter
|
||||
float r_gamma = 1.8f;
|
||||
r_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, r_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float g_gamma = 1.8f;
|
||||
g_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, g_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float b_gamma = 1.8f;
|
||||
b_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, b_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
}
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
// 0: 00110110 // row pixel count (minus one)
|
||||
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
// 56: xxxxrrrr // row select bits
|
||||
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
//
|
||||
// .. and back to the start
|
||||
|
||||
|
||||
// initialise the bcd timing values and row selects in the bitstream
|
||||
for(uint8_t row = 0; row < HEIGHT; row++) {
|
||||
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
|
||||
// find the offset of this row and frame in the bitstream
|
||||
uint8_t *p = &bitstream[row * ROW_BYTES + (BCD_FRAME_BYTES * frame)];
|
||||
|
||||
p[ 0] = WIDTH - 1; // row pixel count
|
||||
p[56] = row; // row select
|
||||
|
||||
// set the number of bcd ticks for this frame
|
||||
uint32_t bcd_ticks = (1 << frame);
|
||||
p[57] = (bcd_ticks & 0xff) >> 0;
|
||||
p[58] = (bcd_ticks & 0xff00) >> 8;
|
||||
p[59] = (bcd_ticks & 0xff0000) >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
// setup light sensor adc
|
||||
adc_init();
|
||||
adc_gpio_init(LIGHT_SENSOR);
|
||||
|
||||
gpio_init(COLUMN_CLOCK); gpio_set_dir(COLUMN_CLOCK, GPIO_OUT); gpio_put(COLUMN_CLOCK, false);
|
||||
gpio_init(COLUMN_DATA); gpio_set_dir(COLUMN_DATA, GPIO_OUT); gpio_put(COLUMN_DATA, false);
|
||||
gpio_init(COLUMN_LATCH); gpio_set_dir(COLUMN_LATCH, GPIO_OUT); gpio_put(COLUMN_LATCH, false);
|
||||
gpio_init(COLUMN_BLANK); gpio_set_dir(COLUMN_BLANK, GPIO_OUT); gpio_put(COLUMN_BLANK, true);
|
||||
|
||||
// initialise the row select, and set them to a non-visible row to avoid flashes during setup
|
||||
gpio_init(ROW_BIT_0); gpio_set_dir(ROW_BIT_0, GPIO_OUT); gpio_put(ROW_BIT_0, true);
|
||||
gpio_init(ROW_BIT_1); gpio_set_dir(ROW_BIT_1, GPIO_OUT); gpio_put(ROW_BIT_1, true);
|
||||
gpio_init(ROW_BIT_2); gpio_set_dir(ROW_BIT_2, GPIO_OUT); gpio_put(ROW_BIT_2, true);
|
||||
gpio_init(ROW_BIT_3); gpio_set_dir(ROW_BIT_3, GPIO_OUT); gpio_put(ROW_BIT_3, true);
|
||||
|
||||
sleep_ms(100);
|
||||
|
||||
// configure full output current in register 2
|
||||
|
||||
uint16_t reg1 = 0b1111111111001110;
|
||||
|
||||
// clock the register value to the first 9 driver chips
|
||||
for(int j = 0; j < 9; j++) {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if(reg1 & (1U << (15 - i))) {
|
||||
gpio_put(COLUMN_DATA, true);
|
||||
}else{
|
||||
gpio_put(COLUMN_DATA, false);
|
||||
}
|
||||
sleep_us(10);
|
||||
gpio_put(COLUMN_CLOCK, true);
|
||||
sleep_us(10);
|
||||
gpio_put(COLUMN_CLOCK, false);
|
||||
}
|
||||
}
|
||||
|
||||
// clock the last chip and latch the value
|
||||
for(int i = 0; i < 16; i++) {
|
||||
if(reg1 & (1U << (15 - i))) {
|
||||
gpio_put(COLUMN_DATA, true);
|
||||
}else{
|
||||
gpio_put(COLUMN_DATA, false);
|
||||
}
|
||||
|
||||
sleep_us(10);
|
||||
gpio_put(COLUMN_CLOCK, true);
|
||||
sleep_us(10);
|
||||
gpio_put(COLUMN_CLOCK, false);
|
||||
|
||||
if(i == 4) {
|
||||
gpio_put(COLUMN_LATCH, true);
|
||||
}
|
||||
}
|
||||
gpio_put(COLUMN_LATCH, false);
|
||||
|
||||
// reapply the blank as the above seems to cause a slight glow.
|
||||
// Note, this will produce a brief flash if a visible row is selected (which it shouldn't be)
|
||||
gpio_put(COLUMN_BLANK, false);
|
||||
sleep_us(10);
|
||||
gpio_put(COLUMN_BLANK, true);
|
||||
|
||||
gpio_init(MUTE); gpio_set_dir(MUTE, GPIO_OUT); gpio_put(MUTE, true);
|
||||
|
||||
// setup button inputs
|
||||
gpio_init(SWITCH_A); gpio_pull_up(SWITCH_A);
|
||||
gpio_init(SWITCH_B); gpio_pull_up(SWITCH_B);
|
||||
gpio_init(SWITCH_C); gpio_pull_up(SWITCH_C);
|
||||
gpio_init(SWITCH_D); gpio_pull_up(SWITCH_D);
|
||||
|
||||
gpio_init(SWITCH_SLEEP); gpio_pull_up(SWITCH_SLEEP);
|
||||
|
||||
gpio_init(SWITCH_BRIGHTNESS_UP); gpio_pull_up(SWITCH_BRIGHTNESS_UP);
|
||||
gpio_init(SWITCH_BRIGHTNESS_DOWN); gpio_pull_up(SWITCH_BRIGHTNESS_DOWN);
|
||||
|
||||
gpio_init(SWITCH_VOLUME_UP); gpio_pull_up(SWITCH_VOLUME_UP);
|
||||
gpio_init(SWITCH_VOLUME_DOWN); gpio_pull_up(SWITCH_VOLUME_DOWN);
|
||||
|
||||
// setup the pio if it has not previously been set up
|
||||
bitstream_pio = pio0;
|
||||
if(unicorn == nullptr) {
|
||||
bitstream_sm = pio_claim_unused_sm(bitstream_pio, true);
|
||||
bitstream_sm_offset = pio_add_program(bitstream_pio, &galactic_unicorn_program);
|
||||
}
|
||||
|
||||
pio_gpio_init(bitstream_pio, COLUMN_CLOCK);
|
||||
pio_gpio_init(bitstream_pio, COLUMN_DATA);
|
||||
pio_gpio_init(bitstream_pio, COLUMN_LATCH);
|
||||
pio_gpio_init(bitstream_pio, COLUMN_BLANK);
|
||||
|
||||
pio_gpio_init(bitstream_pio, ROW_BIT_0);
|
||||
pio_gpio_init(bitstream_pio, ROW_BIT_1);
|
||||
pio_gpio_init(bitstream_pio, ROW_BIT_2);
|
||||
pio_gpio_init(bitstream_pio, ROW_BIT_3);
|
||||
|
||||
// set the blank and row pins to be high, then set all led driving pins as outputs.
|
||||
// This order is important to avoid a momentary flash
|
||||
const uint pins_to_set = 1 << COLUMN_BLANK | 0b1111 << ROW_BIT_0;
|
||||
pio_sm_set_pins_with_mask(bitstream_pio, bitstream_sm, pins_to_set, pins_to_set);
|
||||
pio_sm_set_consecutive_pindirs(bitstream_pio, bitstream_sm, COLUMN_CLOCK, 8, true);
|
||||
|
||||
pio_sm_config c = galactic_unicorn_program_get_default_config(bitstream_sm_offset);
|
||||
|
||||
// osr shifts right, autopull on, autopull threshold 8
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
|
||||
// configure out, set, and sideset pins
|
||||
sm_config_set_out_pins(&c, ROW_BIT_0, 4);
|
||||
sm_config_set_set_pins(&c, COLUMN_DATA, 3);
|
||||
sm_config_set_sideset_pins(&c, COLUMN_CLOCK);
|
||||
|
||||
// join fifos as only tx needed (gives 8 deep fifo instead of 4)
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
// setup dma transfer for pixel data to the pio
|
||||
//if(unicorn == nullptr) {
|
||||
dma_channel = dma_claim_unused_channel(true);
|
||||
dma_ctrl_channel = dma_claim_unused_channel(true);
|
||||
//}
|
||||
dma_channel_config ctrl_config = dma_channel_get_default_config(dma_ctrl_channel);
|
||||
channel_config_set_transfer_data_size(&ctrl_config, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&ctrl_config, false);
|
||||
channel_config_set_write_increment(&ctrl_config, false);
|
||||
channel_config_set_chain_to(&ctrl_config, dma_channel);
|
||||
|
||||
dma_channel_configure(
|
||||
dma_ctrl_channel,
|
||||
&ctrl_config,
|
||||
&dma_hw->ch[dma_channel].read_addr,
|
||||
&bitstream_addr,
|
||||
1,
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
||||
channel_config_set_bswap(&config, false); // byte swap to reverse little endian
|
||||
channel_config_set_dreq(&config, pio_get_dreq(bitstream_pio, bitstream_sm, true));
|
||||
channel_config_set_chain_to(&config, dma_ctrl_channel);
|
||||
|
||||
dma_channel_configure(
|
||||
dma_channel,
|
||||
&config,
|
||||
&bitstream_pio->txf[bitstream_sm],
|
||||
NULL,
|
||||
BITSTREAM_LENGTH / 4,
|
||||
false);
|
||||
|
||||
pio_sm_init(bitstream_pio, bitstream_sm, bitstream_sm_offset, &c);
|
||||
|
||||
pio_sm_set_enabled(bitstream_pio, bitstream_sm, true);
|
||||
|
||||
// start the control channel
|
||||
dma_start_channel_mask(1u << dma_ctrl_channel);
|
||||
|
||||
|
||||
// setup audio pio program
|
||||
audio_pio = pio0;
|
||||
if(unicorn == nullptr) {
|
||||
audio_sm = pio_claim_unused_sm(audio_pio, true);
|
||||
audio_sm_offset = pio_add_program(audio_pio, &audio_i2s_program);
|
||||
}
|
||||
|
||||
pio_gpio_init(audio_pio, I2S_DATA);
|
||||
pio_gpio_init(audio_pio, I2S_BCLK);
|
||||
pio_gpio_init(audio_pio, I2S_LRCLK);
|
||||
|
||||
audio_i2s_program_init(audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
|
||||
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
|
||||
uint32_t divider = system_clock_frequency * 4 / SYSTEM_FREQ; // avoid arithmetic overflow
|
||||
pio_sm_set_clkdiv_int_frac(audio_pio, audio_sm, divider >> 8u, divider & 0xffu);
|
||||
|
||||
audio_dma_channel = dma_claim_unused_channel(true);
|
||||
dma_channel_config audio_config = dma_channel_get_default_config(audio_dma_channel);
|
||||
channel_config_set_transfer_data_size(&audio_config, DMA_SIZE_16);
|
||||
//channel_config_set_bswap(&audio_config, false); // byte swap to reverse little endian
|
||||
channel_config_set_dreq(&audio_config, pio_get_dreq(audio_pio, audio_sm, true));
|
||||
dma_channel_configure(audio_dma_channel, &audio_config, &audio_pio->txf[audio_sm], NULL, 0, false);
|
||||
|
||||
dma_channel_set_irq0_enabled(audio_dma_channel, true);
|
||||
|
||||
if(unicorn == nullptr) {
|
||||
irq_add_shared_handler(DMA_IRQ_0, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
unicorn = this;
|
||||
}
|
||||
|
||||
void GalacticUnicorn::clear() {
|
||||
if(unicorn == this) {
|
||||
for(uint8_t y = 0; y < HEIGHT; y++) {
|
||||
for(uint8_t x = 0; x < WIDTH; x++) {
|
||||
set_pixel(x, y, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::dma_safe_abort(uint channel) {
|
||||
// Tear down the DMA channel.
|
||||
// This is copied from: https://github.com/raspberrypi/pico-sdk/pull/744/commits/5e0e8004dd790f0155426e6689a66e08a83cd9fc
|
||||
uint32_t irq0_save = dma_hw->inte0 & (1u << channel);
|
||||
hw_clear_bits(&dma_hw->inte0, irq0_save);
|
||||
|
||||
dma_hw->abort = 1u << channel;
|
||||
|
||||
// To fence off on in-flight transfers, the BUSY bit should be polled
|
||||
// rather than the ABORT bit, because the ABORT bit can clear prematurely.
|
||||
while (dma_hw->ch[channel].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS) tight_loop_contents();
|
||||
|
||||
// Clear the interrupt (if any) and restore the interrupt masks.
|
||||
dma_hw->ints0 = 1u << channel;
|
||||
hw_set_bits(&dma_hw->inte0, irq0_save);
|
||||
}
|
||||
|
||||
void GalacticUnicorn::play_sample(uint8_t *data, uint32_t length) {
|
||||
stop_playing();
|
||||
|
||||
if(unicorn == this) {
|
||||
// Restart the audio SM and start a new DMA transfer
|
||||
pio_sm_set_enabled(audio_pio, audio_sm, true);
|
||||
dma_channel_transfer_from_buffer_now(audio_dma_channel, data, length / 2);
|
||||
play_mode = PLAYING_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::play_synth() {
|
||||
if(play_mode != PLAYING_SYNTH) {
|
||||
stop_playing();
|
||||
}
|
||||
|
||||
if(unicorn == this && play_mode == NOT_PLAYING) {
|
||||
// Nothing is playing, so we can set up the first buffer straight away
|
||||
current_buffer = 0;
|
||||
|
||||
populate_next_synth();
|
||||
|
||||
// Restart the audio SM and start a new DMA transfer
|
||||
pio_sm_set_enabled(audio_pio, audio_sm, true);
|
||||
|
||||
play_mode = PLAYING_SYNTH;
|
||||
|
||||
next_audio_sequence();
|
||||
}
|
||||
}
|
||||
|
||||
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_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;
|
||||
|
||||
populate_next_synth();
|
||||
}
|
||||
else {
|
||||
play_mode = NOT_PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::populate_next_synth() {
|
||||
int16_t *samples = tone_buffers[current_buffer];
|
||||
for(uint i = 0; i < TONE_BUFFER_SIZE; i++) {
|
||||
samples[i] = synth.get_audio_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::stop_playing() {
|
||||
if(unicorn == this) {
|
||||
// Stop the audio SM
|
||||
pio_sm_set_enabled(audio_pio, audio_sm, false);
|
||||
|
||||
// Reset the I2S pins to avoid popping when audio is suddenly stopped
|
||||
const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
|
||||
pio_sm_set_pins_with_mask(audio_pio, audio_sm, 0, pins_to_clear);
|
||||
|
||||
// Abort any in-progress DMA transfer
|
||||
dma_safe_abort(audio_dma_channel);
|
||||
|
||||
play_mode = NOT_PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
AudioChannel& GalacticUnicorn::synth_channel(uint channel) {
|
||||
assert(channel < PicoSynth::CHANNEL_COUNT);
|
||||
return synth.channels[channel];
|
||||
}
|
||||
|
||||
void GalacticUnicorn::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
|
||||
|
||||
// make those coordinates sane
|
||||
x = (WIDTH - 1) - x;
|
||||
y = (HEIGHT - 1) - y;
|
||||
|
||||
r = (r * this->brightness) >> 8;
|
||||
g = (g * this->brightness) >> 8;
|
||||
b = (b * this->brightness) >> 8;
|
||||
|
||||
uint16_t gamma_r = r_gamma_lut[r];
|
||||
uint16_t gamma_g = g_gamma_lut[g];
|
||||
uint16_t gamma_b = b_gamma_lut[b];
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
// 0: 00110110 // row pixel count (minus one)
|
||||
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
// 56: xxxxrrrr // row select bits
|
||||
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
//
|
||||
// .. and back to the start
|
||||
|
||||
// set the appropriate bits in the separate bcd frames
|
||||
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
|
||||
uint8_t *p = &bitstream[y * ROW_BYTES + (BCD_FRAME_BYTES * frame) + 1 + x];
|
||||
|
||||
uint8_t red_bit = gamma_r & 0b1;
|
||||
uint8_t green_bit = gamma_g & 0b1;
|
||||
uint8_t blue_bit = gamma_b & 0b1;
|
||||
|
||||
*p = (blue_bit << 0) | (green_bit << 1) | (red_bit << 2);
|
||||
|
||||
gamma_r >>= 1;
|
||||
gamma_g >>= 1;
|
||||
gamma_b >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void GalacticUnicorn::set_brightness(float value) {
|
||||
value = value < 0.0f ? 0.0f : value;
|
||||
value = value > 1.0f ? 1.0f : value;
|
||||
this->brightness = floor(value * 256.0f);
|
||||
}
|
||||
|
||||
float GalacticUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
}
|
||||
|
||||
void GalacticUnicorn::adjust_brightness(float delta) {
|
||||
this->set_brightness(this->get_brightness() + delta);
|
||||
}
|
||||
|
||||
void GalacticUnicorn::set_volume(float value) {
|
||||
value = value < 0.0f ? 0.0f : value;
|
||||
value = value > 1.0f ? 1.0f : value;
|
||||
this->volume = floor(value * 255.0f);
|
||||
}
|
||||
|
||||
float GalacticUnicorn::get_volume() {
|
||||
return this->volume / 255.0f;
|
||||
}
|
||||
|
||||
void GalacticUnicorn::adjust_volume(float delta) {
|
||||
this->set_volume(this->get_volume() + delta);
|
||||
}
|
||||
|
||||
void GalacticUnicorn::update(PicoGraphics *graphics) {
|
||||
if(unicorn == this) {
|
||||
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
|
||||
uint32_t *p = (uint32_t *)graphics->frame_buffer;
|
||||
for(size_t j = 0; j < 53 * 11; j++) {
|
||||
int x = j % 53;
|
||||
int y = j / 53;
|
||||
|
||||
uint32_t col = *p;
|
||||
uint8_t r = (col & 0xff0000) >> 16;
|
||||
uint8_t g = (col & 0x00ff00) >> 8;
|
||||
uint8_t b = (col & 0x0000ff) >> 0;
|
||||
p++;
|
||||
|
||||
set_pixel(x, y, r, g, b);
|
||||
}
|
||||
}
|
||||
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
|
||||
uint16_t *p = (uint16_t *)graphics->frame_buffer;
|
||||
for(size_t j = 0; j < 53 * 11; j++) {
|
||||
int x = j % 53;
|
||||
int y = j / 53;
|
||||
|
||||
uint16_t col = __builtin_bswap16(*p);
|
||||
uint8_t r = (col & 0b1111100000000000) >> 8;
|
||||
uint8_t g = (col & 0b0000011111100000) >> 3;
|
||||
uint8_t b = (col & 0b0000000000011111) << 3;
|
||||
p++;
|
||||
|
||||
set_pixel(x, y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GalacticUnicorn::is_pressed(uint8_t button) {
|
||||
return !gpio_get(button);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
#pragma once
|
||||
|
||||
#include "hardware/pio.h"
|
||||
#include "pico_graphics.hpp"
|
||||
#include "../pico_synth/pico_synth.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
class GalacticUnicorn {
|
||||
public:
|
||||
static const int WIDTH = 53;
|
||||
static const int HEIGHT = 11;
|
||||
|
||||
// pin assignments
|
||||
static const uint8_t COLUMN_CLOCK = 13;
|
||||
static const uint8_t COLUMN_DATA = 14;
|
||||
static const uint8_t COLUMN_LATCH = 15;
|
||||
static const uint8_t COLUMN_BLANK = 16;
|
||||
|
||||
static const uint8_t ROW_BIT_0 = 17;
|
||||
static const uint8_t ROW_BIT_1 = 18;
|
||||
static const uint8_t ROW_BIT_2 = 19;
|
||||
static const uint8_t ROW_BIT_3 = 20;
|
||||
|
||||
static const uint8_t LIGHT_SENSOR = 28;
|
||||
|
||||
static const uint8_t MUTE = 22;
|
||||
|
||||
static const uint8_t I2S_DATA = 9;
|
||||
static const uint8_t I2S_BCLK = 10;
|
||||
static const uint8_t I2S_LRCLK = 11;
|
||||
|
||||
static const uint8_t I2C_SDA = 4;
|
||||
static const uint8_t I2C_SCL = 5;
|
||||
|
||||
static const uint8_t SWITCH_A = 0;
|
||||
static const uint8_t SWITCH_B = 1;
|
||||
static const uint8_t SWITCH_C = 3;
|
||||
static const uint8_t SWITCH_D = 6;
|
||||
|
||||
static const uint8_t SWITCH_SLEEP = 27;
|
||||
|
||||
static const uint8_t SWITCH_VOLUME_UP = 7;
|
||||
static const uint8_t SWITCH_VOLUME_DOWN = 8;
|
||||
static const uint8_t SWITCH_BRIGHTNESS_UP = 21;
|
||||
static const uint8_t SWITCH_BRIGHTNESS_DOWN = 26;
|
||||
|
||||
private:
|
||||
static const uint32_t ROW_COUNT = 11;
|
||||
static const uint32_t BCD_FRAME_COUNT = 14;
|
||||
static const uint32_t BCD_FRAME_BYTES = 60;
|
||||
static const uint32_t ROW_BYTES = BCD_FRAME_COUNT * BCD_FRAME_BYTES;
|
||||
static const uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES);
|
||||
static const uint SYSTEM_FREQ = 22050;
|
||||
|
||||
private:
|
||||
static PIO bitstream_pio;
|
||||
static uint bitstream_sm;
|
||||
static uint bitstream_sm_offset;
|
||||
|
||||
static PIO audio_pio;
|
||||
static uint audio_sm;
|
||||
static uint audio_sm_offset;
|
||||
|
||||
uint16_t brightness = 256;
|
||||
uint16_t volume = 127;
|
||||
|
||||
// must be aligned for 32bit dma transfer
|
||||
alignas(4) uint8_t bitstream[BITSTREAM_LENGTH] = {0};
|
||||
const uint32_t bitstream_addr = (uint32_t)bitstream;
|
||||
static GalacticUnicorn* unicorn;
|
||||
static void dma_complete();
|
||||
|
||||
static const uint NUM_TONE_BUFFERS = 2;
|
||||
static const uint TONE_BUFFER_SIZE = 4;
|
||||
int16_t tone_buffers[NUM_TONE_BUFFERS][TONE_BUFFER_SIZE] = {0};
|
||||
uint current_buffer = 0;
|
||||
|
||||
PicoSynth synth;
|
||||
|
||||
enum PlayMode {
|
||||
PLAYING_BUFFER,
|
||||
//PLAYING_TONE,
|
||||
PLAYING_SYNTH,
|
||||
NOT_PLAYING
|
||||
};
|
||||
PlayMode play_mode = NOT_PLAYING;
|
||||
|
||||
public:
|
||||
~GalacticUnicorn();
|
||||
|
||||
void init();
|
||||
static inline void pio_program_init(PIO pio, uint sm, uint offset);
|
||||
|
||||
void clear();
|
||||
|
||||
void update(PicoGraphics *graphics);
|
||||
|
||||
void set_brightness(float value);
|
||||
float get_brightness();
|
||||
void adjust_brightness(float delta);
|
||||
|
||||
void set_volume(float value);
|
||||
float get_volume();
|
||||
void adjust_volume(float delta);
|
||||
|
||||
private:
|
||||
void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b);
|
||||
public:
|
||||
|
||||
uint16_t light();
|
||||
|
||||
bool is_pressed(uint8_t button);
|
||||
|
||||
void play_sample(uint8_t *data, uint32_t length);
|
||||
void play_synth();
|
||||
void stop_playing();
|
||||
AudioChannel& synth_channel(uint channel);
|
||||
|
||||
private:
|
||||
void partial_teardown();
|
||||
void dma_safe_abort(uint channel);
|
||||
void next_audio_sequence();
|
||||
void populate_next_synth();
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
.program galactic_unicorn
|
||||
.side_set 1 opt
|
||||
|
||||
; out pins:
|
||||
;
|
||||
; - 3: row select bit 0
|
||||
; - 4: row select bit 1
|
||||
; - 5: row select bit 2
|
||||
; - 6: row select bit 3
|
||||
|
||||
; set pins:
|
||||
;
|
||||
; - 0: column data (base)
|
||||
; - 1: column latch
|
||||
; - 2: column blank
|
||||
|
||||
; sideset pin:
|
||||
;
|
||||
; - 0: column clock
|
||||
|
||||
; for each row:
|
||||
; for each bcd frame:
|
||||
; 0: 00110110 // row pixel count (minus one)
|
||||
; 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
; 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
; 56: xxxxrrrr // row select bits
|
||||
; 57 - 59: tttttttt, tttttttt, tttttttt, // bcd tick count (0-65536)
|
||||
;
|
||||
; .. and back to the start
|
||||
|
||||
|
||||
.wrap_target
|
||||
|
||||
; loop over row pixels
|
||||
out y, 8 ; get row pixel count (minus 1 because test is pre decrement)
|
||||
pixels:
|
||||
|
||||
; red bit
|
||||
out x, 1 side 0 [1] ; pull in blue bit from OSR into register x, clear clock
|
||||
set pins, 0b100 ; clear data bit, blank high
|
||||
jmp !x endb ; if bit was zero jump
|
||||
set pins, 0b101 ; set data bit, blank high
|
||||
endb:
|
||||
nop side 1 [2] ; clock in bit
|
||||
|
||||
; green bit
|
||||
out x, 1 side 0 [1] ; pull in green bit from OSR into register X, clear clock
|
||||
set pins, 0b100 ; clear data bit, blank high
|
||||
jmp !x endg ; if bit was zero jump
|
||||
set pins, 0b101 ; set data bit, blank high
|
||||
endg:
|
||||
nop side 1 [2] ; clock in bit
|
||||
|
||||
; blue bit
|
||||
out x, 1 side 0 [1] ; pull in red bit from OSR into register X, clear clock
|
||||
set pins, 0b100 ; clear data bit, blank high
|
||||
jmp !x endr ; if bit was zero jump
|
||||
set pins, 0b101 ; set data bit, blank high
|
||||
endr:
|
||||
out null, 5 side 1 [2] ; clock in bit
|
||||
|
||||
;out null, 5 side 0 ; discard the five dummy bits for this pixel
|
||||
|
||||
jmp y-- pixels
|
||||
|
||||
out null, 16 ; discard dummy bytes
|
||||
|
||||
out pins, 8 ; output row select
|
||||
|
||||
set pins, 0b110 [5] ; latch high, blank high
|
||||
set pins, 0b000 ; blank low (enable output)
|
||||
|
||||
; loop over bcd delay period
|
||||
out y, 24 ; get bcd delay counter value
|
||||
bcd_delay:
|
||||
jmp y-- bcd_delay
|
||||
|
||||
set pins 0b100 ; blank high (disable output)
|
||||
|
||||
.wrap
|
|
@ -276,8 +276,6 @@ namespace pimoroni {
|
|||
void PicoGraphics::line(Point p1, Point p2) {
|
||||
// fast horizontal line
|
||||
if(p1.y == p2.y) {
|
||||
p1 = p1.clamp(clip);
|
||||
p2 = p2.clamp(clip);
|
||||
int32_t start = std::min(p1.x, p2.x);
|
||||
int32_t end = std::max(p1.x, p2.x);
|
||||
pixel_span(Point(start, p1.y), end - start);
|
||||
|
@ -286,13 +284,11 @@ namespace pimoroni {
|
|||
|
||||
// fast vertical line
|
||||
if(p1.x == p2.x) {
|
||||
p1 = p1.clamp(clip);
|
||||
p2 = p2.clamp(clip);
|
||||
int32_t start = std::min(p1.y, p2.y);
|
||||
int32_t length = std::max(p1.y, p2.y) - start;
|
||||
Point dest(p1.x, start);
|
||||
while(length--) {
|
||||
set_pixel(dest);
|
||||
pixel(dest);
|
||||
dest.y++;
|
||||
}
|
||||
return;
|
||||
|
@ -314,7 +310,7 @@ namespace pimoroni {
|
|||
int32_t y = p1.y << 16;
|
||||
while(s--) {
|
||||
Point p(x, y >> 16);
|
||||
if(clip.contains(p)) set_pixel(p);
|
||||
pixel(p);
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
|
@ -327,7 +323,7 @@ namespace pimoroni {
|
|||
int32_t x = p1.x << 16;
|
||||
while(s--) {
|
||||
Point p(x >> 16, y);
|
||||
if(clip.contains(p)) set_pixel(p);
|
||||
pixel(p);
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
|
|
|
@ -9,10 +9,11 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
void PicoGraphics_PenRGB888::set_pen(uint c) {
|
||||
color = RGB(c, c, c).to_rgb888();
|
||||
color = c;
|
||||
}
|
||||
void PicoGraphics_PenRGB888::set_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
color = RGB(r, g, b).to_rgb888();
|
||||
src_color = {r, g, b};
|
||||
color = src_color.to_rgb888();
|
||||
}
|
||||
int PicoGraphics_PenRGB888::create_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return RGB(r, g, b).to_rgb888();
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
#include "pico_synth.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
uint32_t prng_xorshift_state = 0x32B71700;
|
||||
|
||||
uint32_t prng_xorshift_next() {
|
||||
uint32_t x = prng_xorshift_state;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
prng_xorshift_state = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
int32_t prng_normal() {
|
||||
// rough approximation of a normal distribution
|
||||
uint32_t r0 = prng_xorshift_next();
|
||||
uint32_t r1 = prng_xorshift_next();
|
||||
uint32_t n = ((r0 & 0xffff) + (r1 & 0xffff) + (r0 >> 16) + (r1 >> 16)) / 2;
|
||||
return n - 0xffff;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void AudioChannel::restore() {
|
||||
// Put all the parameters back to their initial values
|
||||
waveforms = 0;
|
||||
frequency = 660;
|
||||
volume = 0xffff;
|
||||
|
||||
attack_ms = 2;
|
||||
decay_ms = 6;
|
||||
sustain = 0xffff;
|
||||
release_ms = 1;
|
||||
pulse_width = 0x7fff;
|
||||
noise = 0;
|
||||
}
|
||||
|
||||
bool PicoSynth::is_audio_playing() {
|
||||
if(volume == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool any_channel_playing = false;
|
||||
for(uint c = 0; c < CHANNEL_COUNT; c++) {
|
||||
if(channels[c].volume > 0 && channels[c].adsr_phase != ADSRPhase::OFF) {
|
||||
any_channel_playing = true;
|
||||
}
|
||||
}
|
||||
|
||||
return any_channel_playing;
|
||||
}
|
||||
|
||||
int16_t PicoSynth::get_audio_frame() {
|
||||
int32_t sample = 0; // used to combine channel output
|
||||
|
||||
for(uint c = 0; c < CHANNEL_COUNT; c++) {
|
||||
|
||||
auto &channel = channels[c];
|
||||
|
||||
// increment the waveform position counter. this provides an
|
||||
// Q16 fixed point value representing how far through
|
||||
// the current waveform we are
|
||||
channel.waveform_offset += ((channel.frequency * 256) << 8) / sample_rate;
|
||||
|
||||
if(channel.adsr_phase == ADSRPhase::OFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if((channel.adsr_frame >= channel.adsr_end_frame) && (channel.adsr_phase != ADSRPhase::SUSTAIN)) {
|
||||
switch (channel.adsr_phase) {
|
||||
case ADSRPhase::ATTACK:
|
||||
channel.trigger_decay();
|
||||
break;
|
||||
case ADSRPhase::DECAY:
|
||||
channel.trigger_sustain();
|
||||
break;
|
||||
case ADSRPhase::RELEASE:
|
||||
channel.off();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
channel.adsr_level += channel.adsr_step;
|
||||
channel.adsr_frame++;
|
||||
|
||||
if(channel.waveform_offset & 0x10000) {
|
||||
// if the waveform offset overflows then generate a new
|
||||
// random noise sample
|
||||
channel.noise = prng_normal();
|
||||
}
|
||||
|
||||
channel.waveform_offset &= 0xffff;
|
||||
|
||||
// check if any waveforms are active for this channel
|
||||
if(channel.waveforms) {
|
||||
uint8_t waveform_count = 0;
|
||||
int32_t channel_sample = 0;
|
||||
|
||||
if(channel.waveforms & Waveform::NOISE) {
|
||||
channel_sample += channel.noise;
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
if(channel.waveforms & Waveform::SAW) {
|
||||
channel_sample += (int32_t)channel.waveform_offset - 0x7fff;
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
// creates a triangle wave of ^
|
||||
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
|
||||
channel_sample += int32_t(0x7fff) - ((int32_t(channel.waveform_offset) - int32_t(0x7fff)) * 2);
|
||||
}
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
if(channel.waveforms & Waveform::SQUARE) {
|
||||
channel_sample += (channel.waveform_offset < channel.pulse_width) ? 0x7fff : -0x7fff;
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
if(channel.waveforms & Waveform::SINE) {
|
||||
// the sine_waveform sample contains 256 samples in
|
||||
// total so we'll just use the most significant bits
|
||||
// of the current waveform position to index into it
|
||||
channel_sample += sine_waveform[channel.waveform_offset >> 8];
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
if(channel.waveforms & Waveform::WAVE) {
|
||||
channel_sample += channel.wave_buffer[channel.wave_buf_pos];
|
||||
if(++channel.wave_buf_pos == 64) {
|
||||
channel.wave_buf_pos = 0;
|
||||
if(channel.wave_buffer_callback)
|
||||
channel.wave_buffer_callback(channel);
|
||||
}
|
||||
waveform_count++;
|
||||
}
|
||||
|
||||
channel_sample = channel_sample / waveform_count;
|
||||
|
||||
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;
|
||||
|
||||
// apply channel filter
|
||||
//if (channel.filter_enable) {
|
||||
//float filter_epow = 1 - expf(-(1.0f / 22050.0f) * 2.0f * pi * int32_t(channel.filter_cutoff_frequency));
|
||||
//channel_sample += (channel_sample - channel.filter_last_sample) * filter_epow;
|
||||
//}
|
||||
|
||||
//channel.filter_last_sample = channel_sample;
|
||||
|
||||
// combine channel sample into the final sample
|
||||
sample += channel_sample;
|
||||
}
|
||||
}
|
||||
|
||||
sample = (int64_t(sample) * int32_t(volume)) >> 16;
|
||||
|
||||
// clip result to 16-bit
|
||||
sample = sample <= -0x8000 ? -0x8000 : (sample > 0x7fff ? 0x7fff : sample);
|
||||
return sample;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
//#include <string>
|
||||
//#include <array>
|
||||
//#include <cstdint>
|
||||
//#include <algorithm>
|
||||
//#include <vector>
|
||||
//#include <functional>
|
||||
|
||||
#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
|
||||
// the user.
|
||||
//
|
||||
// - Attack: number of milliseconds it takes for a note to hit full volume
|
||||
// - Decay: number of milliseconds it takes for a note to settle to sustain volume
|
||||
// - Sustain: percentage of full volume that the note sustains at (duration implied by other factors)
|
||||
// - Release: number of milliseconds it takes for a note to reduce to zero volume after it has ended
|
||||
//
|
||||
// Attack (750ms) - Decay (500ms) -------- Sustain ----- Release (250ms)
|
||||
//
|
||||
// + + + +
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// v v v v
|
||||
// 0ms 1000ms 2000ms 3000ms 4000ms
|
||||
//
|
||||
// | XXXX | | | |
|
||||
// | X X|XX | | |
|
||||
// | X | XXX | | |
|
||||
// | X | XXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXX| |
|
||||
// | X | | |X |
|
||||
// | X | | |X |
|
||||
// | X | | | X |
|
||||
// | X | | | X |
|
||||
// | X | | | X |
|
||||
// | X | | | X |
|
||||
// | X | | | X |
|
||||
// | X | | | X |
|
||||
// | X + + + | + + + | + + + | + + + | +
|
||||
// | X | | | | | | | | | | | | | | | | |
|
||||
// |X | | | | | | | | | | | | | | | | |
|
||||
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--->
|
||||
|
||||
enum Waveform {
|
||||
NOISE = 128,
|
||||
SQUARE = 64,
|
||||
SAW = 32,
|
||||
TRIANGLE = 16,
|
||||
SINE = 8,
|
||||
WAVE = 1
|
||||
};
|
||||
|
||||
enum class ADSRPhase : uint8_t {
|
||||
ATTACK,
|
||||
DECAY,
|
||||
SUSTAIN,
|
||||
RELEASE,
|
||||
OFF
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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 = UINT16_MAX; // 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 = UINT16_MAX; // sustain volume
|
||||
uint16_t release_ms = 1; // release period
|
||||
uint16_t pulse_width = 0x7fff; // duty cycle of square wave (default 50%)
|
||||
int16_t noise = 0; // current noise value
|
||||
|
||||
uint32_t waveform_offset = 0; // voice offset (Q8)
|
||||
|
||||
int32_t filter_last_sample = 0;
|
||||
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_end_frame = 0; // frame target at which the ADSR changes to the next phase
|
||||
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
|
||||
|
||||
void *user_data = nullptr;
|
||||
void (*wave_buffer_callback)(AudioChannel &channel);
|
||||
|
||||
void trigger_attack();
|
||||
void trigger_decay();
|
||||
void trigger_sustain();
|
||||
void trigger_release();
|
||||
void off();
|
||||
void restore();
|
||||
};
|
||||
|
||||
class PicoSynth {
|
||||
public:
|
||||
const uint16_t volume = 0x2fff;
|
||||
|
||||
static const uint CHANNEL_COUNT = 8;
|
||||
AudioChannel channels[CHANNEL_COUNT];
|
||||
|
||||
int16_t get_audio_frame();
|
||||
bool is_audio_playing();
|
||||
};
|
||||
|
||||
constexpr float pi = 3.14159265358979323846f;
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import time
|
||||
import random
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
colour = (230, 150, 0)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup():
|
||||
global width, height, lifetime, age
|
||||
width = GalacticUnicorn.WIDTH
|
||||
height = GalacticUnicorn.HEIGHT
|
||||
lifetime = [[0.0 for y in range(height)] for x in range(width)]
|
||||
age = [[0.0 for y in range(height)] for x in range(width)]
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
age[x][y] = random.uniform(0.0, 1.0) * lifetime[x][y]
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] < lifetime[x][y] * 0.3:
|
||||
graphics.set_pen(graphics.create_pen(colour[0], colour[1], colour[2]))
|
||||
elif age[x][y] < lifetime[x][y] * 0.5:
|
||||
decay = (lifetime[x][y] * 0.5 - age[x][y]) * 5.0
|
||||
graphics.set_pen(graphics.create_pen(int(decay * colour[0]), int(decay * colour[1]), int(decay * colour[2])))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def update():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] >= lifetime[x][y]:
|
||||
age[x][y] = 0.0
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
|
||||
age[x][y] += 0.025
|
||||
|
||||
|
||||
setup()
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
draw()
|
||||
update()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,117 @@
|
|||
import time
|
||||
import math
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = GalacticUnicorn.WIDTH
|
||||
height = GalacticUnicorn.HEIGHT
|
||||
|
||||
|
||||
def gradient(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
graphics.set_pen(graphics.create_pen(int((r * x) / 52), int((g * x) / 52), int((b * x) / 52)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def grid(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
if (x + y) % 2 == 0:
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def outline_text(text):
|
||||
ms = time.ticks_ms()
|
||||
|
||||
graphics.set_font("bitmap8")
|
||||
v = int((math.sin(ms / 100.0) + 1.0) * 127.0)
|
||||
w = graphics.measure_text(text, 1)
|
||||
|
||||
x = int(53 / 2 - w / 2 + 1)
|
||||
y = 2
|
||||
|
||||
graphics.set_pen(0)
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(v, v, v))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
test = (time_ms // 1000) % 5
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if test == 0:
|
||||
print("grid pattern")
|
||||
grid(255, 255, 255)
|
||||
elif test == 1:
|
||||
print("red gradient")
|
||||
gradient(255, 0, 0)
|
||||
elif test == 2:
|
||||
print("green gradient")
|
||||
gradient(0, 255, 0)
|
||||
elif test == 3:
|
||||
print("blue gradient")
|
||||
gradient(0, 0, 255)
|
||||
elif test == 4:
|
||||
print("white gradient")
|
||||
gradient(255, 255, 255)
|
||||
|
||||
text = ""
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
text = "Button A"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_B):
|
||||
text = "Button B"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_C):
|
||||
text = "Button C"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_D):
|
||||
text = "Button D"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
|
||||
text = "Louder!"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
|
||||
text = "Quieter"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
text = "Brighter!"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
text = "Darker"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||
text = "Zzz... zzz..."
|
||||
|
||||
outline_text(text)
|
||||
|
||||
gu.update(graphics)
|
|
@ -0,0 +1,936 @@
|
|||
import gc
|
||||
import time
|
||||
import math
|
||||
from machine import Timer
|
||||
from galactic import GalacticUnicorn, Channel
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gc.collect()
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = GalacticUnicorn.WIDTH
|
||||
height = GalacticUnicorn.HEIGHT
|
||||
|
||||
left_channel = bytearray((
|
||||
0xfe, 0xff, 0xfb, 0xff, 0xf9, 0xff, 0xf6, 0xff, 0xee, 0xff, 0xed, 0xff,
|
||||
0xec, 0xff, 0xed, 0xff, 0xec, 0xff, 0xed, 0xff, 0xef, 0xff, 0xf3, 0xff,
|
||||
0x01, 0x00, 0x04, 0x00, 0xe8, 0xff, 0xf6, 0xff, 0x08, 0x00, 0x06, 0x00,
|
||||
0x09, 0x00, 0x10, 0x00, 0x39, 0x00, 0x5c, 0x00, 0x6e, 0x00, 0x65, 0x00,
|
||||
0x6e, 0x00, 0x88, 0x00, 0x80, 0x00, 0x70, 0x00, 0x78, 0x00, 0xab, 0x00,
|
||||
0xa1, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xe1, 0x00, 0x21, 0x01, 0x3c, 0x01,
|
||||
0x3a, 0x01, 0x3f, 0x01, 0x55, 0x01, 0x88, 0x01, 0xe4, 0x01, 0x02, 0x02,
|
||||
0xeb, 0x01, 0xef, 0x01, 0x1c, 0x02, 0x1a, 0x02, 0x3d, 0x02, 0x45, 0x02,
|
||||
0x3f, 0x02, 0x1e, 0x02, 0x1b, 0x02, 0x5d, 0x02, 0xb7, 0x02, 0x3c, 0x03,
|
||||
0x01, 0x03, 0x31, 0x03, 0x5e, 0x03, 0x47, 0x03, 0x8d, 0x03, 0xf2, 0x03,
|
||||
0xe4, 0x03, 0xc7, 0x03, 0x2d, 0x03, 0x32, 0x03, 0xba, 0x03, 0x3e, 0x03,
|
||||
0xb3, 0x02, 0x54, 0x02, 0x14, 0x02, 0xd5, 0x01, 0x4e, 0x01, 0xcc, 0x00,
|
||||
0xee, 0xff, 0xeb, 0xfe, 0x97, 0xfe, 0x08, 0xfe, 0x83, 0xfd, 0xe7, 0xfc,
|
||||
0x52, 0xfc, 0x0b, 0xfc, 0x78, 0xfc, 0xc0, 0xfb, 0xe2, 0xfa, 0x3a, 0xfb,
|
||||
0x57, 0xfb, 0x07, 0xfb, 0x9b, 0xfa, 0x09, 0xfa, 0x18, 0xfa, 0xee, 0xfa,
|
||||
0xb7, 0xfa, 0x1e, 0xfa, 0x4e, 0xf9, 0x39, 0xf9, 0x7d, 0xf9, 0x21, 0xfa,
|
||||
0xcb, 0xfa, 0x9e, 0xfa, 0xa1, 0xfa, 0x92, 0xfa, 0x52, 0xfb, 0x8e, 0xfb,
|
||||
0xa4, 0xfb, 0x5e, 0xfb, 0x03, 0xfc, 0x0c, 0xfd, 0xc3, 0xfd, 0xfe, 0xfd,
|
||||
0xda, 0xfd, 0x6e, 0xfe, 0x1d, 0xff, 0xf6, 0xff, 0x02, 0x00, 0xfc, 0xff,
|
||||
0x4b, 0x00, 0xcc, 0x00, 0x40, 0x01, 0x87, 0x01, 0xf0, 0x01, 0x2e, 0x02,
|
||||
0xf6, 0x02, 0x78, 0x03, 0xa4, 0x03, 0x3f, 0x04, 0x8f, 0x04, 0xdf, 0x04,
|
||||
0x11, 0x05, 0x97, 0x04, 0x05, 0x05, 0x57, 0x05, 0xae, 0x05, 0xb2, 0x05,
|
||||
0x84, 0x05, 0xc2, 0x05, 0x86, 0x05, 0x9f, 0x05, 0xfb, 0x05, 0x0e, 0x06,
|
||||
0x10, 0x06, 0xec, 0x05, 0xc4, 0x05, 0xc1, 0x05, 0xa1, 0x05, 0xa2, 0x06,
|
||||
0xba, 0x06, 0xbd, 0x05, 0xe9, 0x05, 0xf0, 0x05, 0x9a, 0x06, 0xfd, 0x06,
|
||||
0x45, 0x06, 0x3b, 0x06, 0x8d, 0x05, 0x69, 0x05, 0x8e, 0x05, 0x09, 0x05,
|
||||
0xbd, 0x04, 0x5f, 0x04, 0xcd, 0x03, 0xd7, 0x03, 0x74, 0x03, 0x81, 0x02,
|
||||
0x6e, 0x01, 0x21, 0x00, 0x1a, 0xff, 0xe3, 0xfe, 0x4b, 0xfe, 0x3f, 0xfd,
|
||||
0xc3, 0xfb, 0x0d, 0xfb, 0x8a, 0xf9, 0x15, 0xf8, 0x6a, 0xf7, 0x92, 0xf7,
|
||||
0xb3, 0xf7, 0xe6, 0xf7, 0xc3, 0xf6, 0xce, 0xf5, 0xc4, 0xf5, 0xe1, 0xf5,
|
||||
0xdd, 0xf5, 0x0e, 0xf5, 0xa4, 0xf4, 0xe5, 0xf4, 0xe9, 0xf4, 0x60, 0xf4,
|
||||
0xac, 0xf4, 0xba, 0xf4, 0xb2, 0xf5, 0xad, 0xf6, 0xf7, 0xf6, 0x4a, 0xf7,
|
||||
0x63, 0xf7, 0xad, 0xf7, 0xe4, 0xf8, 0x89, 0xf9, 0xc0, 0xf9, 0x80, 0xfa,
|
||||
0x93, 0xfb, 0xac, 0xfc, 0xae, 0xfd, 0x51, 0xfe, 0xf3, 0xfe, 0x91, 0x00,
|
||||
0x12, 0x02, 0x4a, 0x03, 0x60, 0x03, 0x84, 0x03, 0x6a, 0x04, 0xdd, 0x04,
|
||||
0xa3, 0x05, 0x45, 0x06, 0xeb, 0x06, 0xd6, 0x07, 0x74, 0x08, 0xaa, 0x08,
|
||||
0xdd, 0x08, 0x17, 0x09, 0xb8, 0x09, 0x27, 0x0a, 0x60, 0x0a, 0x7e, 0x0a,
|
||||
0xff, 0x0a, 0x18, 0x0b, 0x08, 0x0b, 0x3a, 0x0b, 0x97, 0x0b, 0xfe, 0x0b,
|
||||
0xd7, 0x0b, 0x1c, 0x0b, 0xc8, 0x0a, 0x85, 0x0a, 0x4f, 0x0a, 0x8c, 0x0a,
|
||||
0x4a, 0x0a, 0xc0, 0x09, 0x80, 0x09, 0xf5, 0x08, 0xc0, 0x08, 0xe4, 0x08,
|
||||
0x10, 0x08, 0x4a, 0x06, 0x89, 0x05, 0x51, 0x05, 0x7a, 0x04, 0xd6, 0x02,
|
||||
0x2a, 0x01, 0x5d, 0x00, 0x20, 0x00, 0xb6, 0xff, 0x24, 0xfe, 0xc6, 0xfa,
|
||||
0x8e, 0xf9, 0xfc, 0xf9, 0xe8, 0xf9, 0x67, 0xf8, 0x84, 0xf5, 0x67, 0xf5,
|
||||
0xc7, 0xf6, 0x4c, 0xf6, 0xb9, 0xf5, 0x47, 0xf4, 0x12, 0xf3, 0xa4, 0xf3,
|
||||
0xdd, 0xf3, 0x8f, 0xf3, 0x5f, 0xf2, 0x14, 0xf1, 0xa3, 0xf0, 0xb7, 0xf0,
|
||||
0xa1, 0xf1, 0x89, 0xf2, 0x7a, 0xf2, 0xa5, 0xf2, 0xea, 0xf2, 0xa1, 0xf3,
|
||||
0x5d, 0xf4, 0x01, 0xf5, 0x7a, 0xf5, 0x8a, 0xf5, 0x03, 0xf6, 0x4e, 0xf7,
|
||||
0xe7, 0xf8, 0x41, 0xf9, 0xa7, 0xf9, 0x0b, 0xfb, 0x03, 0xfd, 0x2f, 0xff,
|
||||
0x30, 0x00, 0xfd, 0x00, 0x2c, 0x01, 0xf8, 0x01, 0xa2, 0x03, 0xa6, 0x04,
|
||||
0x55, 0x05, 0xaa, 0x05, 0x00, 0x07, 0x3f, 0x08, 0xd0, 0x08, 0xb4, 0x09,
|
||||
0x30, 0x0a, 0xbc, 0x0a, 0xf0, 0x0b, 0x24, 0x0c, 0xe7, 0x0b, 0x00, 0x0c,
|
||||
0x7a, 0x0c, 0x30, 0x0d, 0xe2, 0x0c, 0x01, 0x0d, 0x4a, 0x0d, 0x9f, 0x0d,
|
||||
0xa8, 0x0d, 0x36, 0x0d, 0x0c, 0x0d, 0x97, 0x0c, 0xad, 0x0c, 0xce, 0x0c,
|
||||
0xe5, 0x0b, 0xb0, 0x0b, 0x59, 0x0b, 0xe7, 0x0a, 0xc0, 0x0a, 0x38, 0x0a,
|
||||
0xb0, 0x09, 0x08, 0x09, 0xbe, 0x08, 0x6d, 0x08, 0xf0, 0x06, 0xbf, 0x05,
|
||||
0xd7, 0x04, 0x0d, 0x05, 0x20, 0x04, 0xe4, 0x02, 0x00, 0x02, 0x35, 0x00,
|
||||
0xd0, 0xfd, 0x28, 0xfd, 0x2d, 0xfc, 0x24, 0xfa, 0xc7, 0xf8, 0xde, 0xf6,
|
||||
0x2c, 0xf6, 0x5d, 0xf6, 0x74, 0xf5, 0x3f, 0xf5, 0x33, 0xf4, 0xb4, 0xf2,
|
||||
0xf9, 0xf2, 0xe3, 0xf2, 0xd6, 0xf2, 0x04, 0xf2, 0xf9, 0xef, 0xf7, 0xee,
|
||||
0xc8, 0xef, 0x12, 0xf1, 0x74, 0xf0, 0xbf, 0xee, 0x3c, 0xf0, 0x79, 0xf1,
|
||||
0x40, 0xf1, 0x5c, 0xf1, 0xd9, 0xf1, 0xf3, 0xf2, 0xdf, 0xf3, 0xcf, 0xf3,
|
||||
0xcb, 0xf3, 0x90, 0xf4, 0x9f, 0xf5, 0x5c, 0xf7, 0x3c, 0xf9, 0x02, 0xfa,
|
||||
0x60, 0xfb, 0xcc, 0xfc, 0x7d, 0xfe, 0x11, 0x00, 0x3e, 0x01, 0x67, 0x02,
|
||||
0x33, 0x03, 0xbf, 0x04, 0x4a, 0x06, 0x2d, 0x07, 0x49, 0x08, 0x43, 0x09,
|
||||
0xb9, 0x0a, 0x54, 0x0c, 0x23, 0x0d, 0xc0, 0x0d, 0x6a, 0x0e, 0x0d, 0x0f,
|
||||
0x1c, 0x0f, 0x48, 0x0f, 0x62, 0x0f, 0xcb, 0x0e, 0xfb, 0x0e, 0x83, 0x0f,
|
||||
0x99, 0x0f, 0x48, 0x0f, 0x30, 0x0f, 0x28, 0x0f, 0x02, 0x0f, 0x6f, 0x0e,
|
||||
0xb5, 0x0d, 0x78, 0x0c, 0xd0, 0x0b, 0x34, 0x0b, 0xc1, 0x0a, 0x18, 0x0a,
|
||||
0x12, 0x09, 0x5b, 0x08, 0x8d, 0x07, 0x72, 0x06, 0x17, 0x05, 0xf9, 0x03,
|
||||
0xae, 0x03, 0xcf, 0x02, 0xd0, 0x00, 0xf5, 0xfe, 0x4c, 0xfe, 0xb3, 0xfd,
|
||||
0x1b, 0xfd, 0x63, 0xfb, 0x9a, 0xf7, 0x62, 0xf6, 0xd7, 0xf7, 0xc8, 0xf7,
|
||||
0x15, 0xf6, 0x6e, 0xf4, 0x22, 0xf3, 0x5f, 0xf3, 0xb8, 0xf3, 0xe3, 0xf2,
|
||||
0x15, 0xf2, 0x3c, 0xf2, 0x19, 0xf1, 0x8c, 0xf1, 0x98, 0xf1, 0x65, 0xef,
|
||||
0xfc, 0xef, 0x7b, 0xf0, 0x76, 0xf1, 0xac, 0xf2, 0x59, 0xf2, 0xb7, 0xf1,
|
||||
0x9e, 0xf2, 0xda, 0xf3, 0x33, 0xf4, 0xca, 0xf4, 0x9d, 0xf4, 0x16, 0xf5,
|
||||
0x21, 0xf6, 0x9e, 0xf7, 0xfd, 0xf7, 0xb8, 0xf8, 0x85, 0xfa, 0x31, 0xfc,
|
||||
0x01, 0xfe, 0x32, 0xff, 0x49, 0x00, 0x8e, 0x01, 0x75, 0x02, 0x74, 0x03,
|
||||
0xa8, 0x04, 0x59, 0x05, 0xaa, 0x06, 0xf9, 0x07, 0x16, 0x09, 0x55, 0x0a,
|
||||
0x5a, 0x0b, 0xa9, 0x0c, 0x2c, 0x0d, 0xee, 0x0d, 0x32, 0x0e, 0x64, 0x0e,
|
||||
0xa0, 0x0e, 0x40, 0x0e, 0xb2, 0x0e, 0x9e, 0x0e, 0xfa, 0x0d, 0xed, 0x0d,
|
||||
0x2e, 0x0e, 0xf5, 0x0e, 0xe1, 0x0e, 0xd1, 0x0d, 0xda, 0x0c, 0x96, 0x0c,
|
||||
0xa2, 0x0c, 0xd7, 0x0b, 0xaf, 0x0a, 0xdd, 0x09, 0x7e, 0x09, 0x19, 0x09,
|
||||
0x23, 0x08, 0x0d, 0x07, 0xb5, 0x06, 0xea, 0x06, 0x21, 0x06, 0x62, 0x04,
|
||||
0x92, 0x02, 0x6d, 0x01, 0xee, 0x00, 0x2b, 0x00, 0x62, 0xfe, 0x5a, 0xfc,
|
||||
0xeb, 0xfa, 0x63, 0xfa, 0xb5, 0xf9, 0xd0, 0xf7, 0xad, 0xf5, 0xaf, 0xf4,
|
||||
0x16, 0xf5, 0x35, 0xf5, 0xf6, 0xf3, 0xc7, 0xf2, 0xea, 0xf1, 0x5b, 0xf2,
|
||||
0xf7, 0xf2, 0x8c, 0xf3, 0xc3, 0xf1, 0x53, 0xef, 0xc9, 0xef, 0x79, 0xf0,
|
||||
0xc7, 0xf0, 0xbb, 0xf0, 0xfa, 0xef, 0x94, 0xef, 0xae, 0xf0, 0x3b, 0xf2,
|
||||
0x3e, 0xf3, 0xe9, 0xf2, 0x05, 0xf3, 0xc9, 0xf3, 0xa7, 0xf4, 0x79, 0xf4,
|
||||
0xe6, 0xf4, 0x56, 0xf6, 0xe7, 0xf7, 0x8c, 0xf9, 0xcd, 0xfa, 0x59, 0xfb,
|
||||
0x4d, 0xfd, 0xd8, 0xff, 0x8d, 0x01, 0x16, 0x02, 0xdf, 0x02, 0x3f, 0x04,
|
||||
0xf0, 0x05, 0x71, 0x07, 0xc6, 0x07, 0x1b, 0x09, 0xa0, 0x0a, 0xd1, 0x0b,
|
||||
0x15, 0x0d, 0x6b, 0x0d, 0x4d, 0x0e, 0x59, 0x0f, 0x33, 0x10, 0xe5, 0x0f,
|
||||
0x4d, 0x0f, 0x1e, 0x10, 0x0d, 0x11, 0x52, 0x11, 0x90, 0x11, 0xf1, 0x10,
|
||||
0xf2, 0x0f, 0x95, 0x0f, 0x45, 0x10, 0x25, 0x10, 0x78, 0x0e, 0xf8, 0x0c,
|
||||
0x90, 0x0c, 0x34, 0x0c, 0x80, 0x0b, 0x40, 0x0a, 0xee, 0x08, 0x5d, 0x08,
|
||||
0x2e, 0x08, 0x32, 0x07, 0x55, 0x05, 0xdc, 0x03, 0x14, 0x03, 0x6e, 0x02,
|
||||
0x5c, 0x00, 0x05, 0xfe, 0x32, 0xfc, 0xe6, 0xfb, 0x39, 0xfb, 0x73, 0xf9,
|
||||
0x14, 0xf7, 0xbe, 0xf4, 0x88, 0xf4, 0x5d, 0xf4, 0xfb, 0xf3, 0x1b, 0xf3,
|
||||
0xa8, 0xf1, 0x3b, 0xf2, 0x5c, 0xf2, 0x18, 0xf2, 0x2e, 0xf1, 0x97, 0xef,
|
||||
0xd2, 0xef, 0x8c, 0xf0, 0x3c, 0xef, 0xd8, 0xed, 0x69, 0xed, 0xc5, 0xee,
|
||||
0x19, 0xf0, 0x67, 0xf0, 0xa4, 0xef, 0x08, 0xf0, 0x46, 0xf1, 0x7b, 0xf2,
|
||||
0x83, 0xf2, 0x6e, 0xf2, 0x99, 0xf2, 0x60, 0xf4, 0xf2, 0xf5, 0xd2, 0xf5,
|
||||
0x34, 0xf6, 0xfc, 0xf7, 0x89, 0xfa, 0x07, 0xfd, 0x6e, 0xfe, 0xec, 0xfe,
|
||||
0xaa, 0xff, 0xb4, 0x00, 0xf7, 0x02, 0x54, 0x04, 0x49, 0x05, 0xe2, 0x05,
|
||||
0xdc, 0x06, 0xb3, 0x08, 0x46, 0x0a, 0xf6, 0x0b, 0x58, 0x0d, 0xff, 0x0d,
|
||||
0x86, 0x0f, 0x67, 0x10, 0x62, 0x10, 0xab, 0x0f, 0xdd, 0x0f, 0x00, 0x11,
|
||||
0x03, 0x12, 0x50, 0x12, 0xd7, 0x11, 0xb9, 0x11, 0x53, 0x12, 0x99, 0x12,
|
||||
0xd9, 0x12, 0x53, 0x11, 0x4a, 0x0f, 0x97, 0x0e, 0x47, 0x0e, 0x7b, 0x0d,
|
||||
0x07, 0x0c, 0xdd, 0x0a, 0xfe, 0x09, 0x9c, 0x09, 0x00, 0x09, 0x09, 0x08,
|
||||
0xfc, 0x06, 0xa6, 0x05, 0xdd, 0x03, 0xfd, 0x01, 0x55, 0x00, 0x0d, 0xff,
|
||||
0x05, 0xfe, 0x91, 0xfc, 0x46, 0xfb, 0x79, 0xf9, 0x92, 0xf7, 0x52, 0xf6,
|
||||
0xaa, 0xf4, 0xd8, 0xf3, 0x8d, 0xf3, 0xe6, 0xf2, 0xb4, 0xf1, 0xc5, 0xf0,
|
||||
0xc6, 0xf0, 0xd8, 0xf0, 0x1a, 0xf1, 0xc7, 0xf0, 0x47, 0xef, 0xf7, 0xee,
|
||||
0xc7, 0xee, 0x47, 0xee, 0x83, 0xee, 0x8a, 0xed, 0x7f, 0xee, 0x57, 0xef,
|
||||
0x94, 0xef, 0x4b, 0xf0, 0x5e, 0xf0, 0xc4, 0xf1, 0x7c, 0xf2, 0xbc, 0xf1,
|
||||
0x90, 0xf2, 0x1b, 0xf4, 0x00, 0xf5, 0x56, 0xf6, 0xa1, 0xf6, 0x54, 0xf7,
|
||||
0x66, 0xf9, 0x83, 0xfb, 0xfc, 0xfd, 0xb3, 0xff, 0xd0, 0xff, 0xe3, 0x00,
|
||||
0x1a, 0x03, 0xa6, 0x04, 0xba, 0x05, 0xaa, 0x06, 0x1f, 0x08, 0xa6, 0x09,
|
||||
0xae, 0x0a, 0xd1, 0x0b, 0xbb, 0x0d, 0x2e, 0x0f, 0xfc, 0x0f, 0x55, 0x10,
|
||||
0xb5, 0x10, 0x05, 0x11, 0xb8, 0x11, 0x6d, 0x12, 0x14, 0x12, 0xb1, 0x11,
|
||||
0x56, 0x11, 0xaf, 0x11, 0xe0, 0x12, 0x9a, 0x12, 0x3b, 0x11, 0x0f, 0x10,
|
||||
0xeb, 0x0f, 0xcb, 0x0f, 0x94, 0x0e, 0xce, 0x0c, 0x8a, 0x0b, 0x36, 0x0b,
|
||||
0xb8, 0x0a, 0xf9, 0x09, 0x35, 0x09, 0xbd, 0x07, 0xb8, 0x06, 0x45, 0x06,
|
||||
0xd4, 0x04, 0x19, 0x03, 0x0e, 0x01, 0x59, 0xff, 0x98, 0xfe, 0x4b, 0xfd,
|
||||
0x97, 0xfb, 0x85, 0xfa, 0x56, 0xf8, 0xc6, 0xf6, 0xe6, 0xf5, 0x5c, 0xf4,
|
||||
0xf2, 0xf2, 0x07, 0xf4, 0xb8, 0xf3, 0x79, 0xf1, 0x64, 0xf0, 0x2e, 0xef,
|
||||
0xf8, 0xef, 0x38, 0xf1, 0x86, 0xf0, 0xde, 0xee, 0x24, 0xed, 0x7d, 0xed,
|
||||
0xad, 0xee, 0x7c, 0xee, 0xb8, 0xed, 0x47, 0xed, 0xfa, 0xed, 0x36, 0xf0,
|
||||
0x1d, 0xf1, 0x47, 0xf0, 0xca, 0xef, 0xd8, 0xf0, 0x80, 0xf3, 0x71, 0xf4,
|
||||
0x77, 0xf4, 0x92, 0xf3, 0xe3, 0xf3, 0x52, 0xf7, 0xd6, 0xf9, 0x5b, 0xfb,
|
||||
0xe9, 0xfb, 0x63, 0xfc, 0x52, 0xff, 0xdf, 0x01, 0xaf, 0x03, 0x8c, 0x04,
|
||||
0xec, 0x03, 0xd6, 0x05, 0x6b, 0x08, 0x95, 0x0a, 0x20, 0x0c, 0x72, 0x0c,
|
||||
0xb4, 0x0d, 0x74, 0x0f, 0x08, 0x11, 0xc4, 0x11, 0x1a, 0x12, 0x4c, 0x12,
|
||||
0x79, 0x12, 0xf7, 0x12, 0x0a, 0x13, 0xa1, 0x12, 0xe6, 0x12, 0xed, 0x12,
|
||||
0x48, 0x13, 0x8a, 0x13, 0x10, 0x13, 0xcf, 0x12, 0xd2, 0x11, 0x97, 0x10,
|
||||
0xb8, 0x0f, 0xe2, 0x0e, 0xaf, 0x0d, 0x3a, 0x0c, 0x3e, 0x0c, 0x71, 0x0a,
|
||||
0x81, 0x08, 0x28, 0x08, 0xc3, 0x07, 0xc2, 0x06, 0x81, 0x04, 0x3a, 0x02,
|
||||
0x85, 0xff, 0x36, 0xfe, 0x15, 0xfd, 0x98, 0xfb, 0xab, 0xf9, 0xb5, 0xf8,
|
||||
0x8e, 0xf6, 0xfa, 0xf4, 0x85, 0xf3, 0x51, 0xf2, 0x59, 0xf1, 0x7e, 0xef,
|
||||
0x44, 0xef, 0xe3, 0xed, 0x0b, 0xee, 0xc2, 0xee, 0x7a, 0xee, 0xd3, 0xed,
|
||||
0x61, 0xed, 0x7f, 0xed, 0xb9, 0xee, 0x4e, 0xee, 0x7e, 0xec, 0xae, 0xeb,
|
||||
0xdd, 0xeb, 0x38, 0xed, 0x2e, 0xef, 0xef, 0xef, 0x21, 0xf0, 0x52, 0xf1,
|
||||
0x7a, 0xf2, 0x16, 0xf4, 0x2d, 0xf5, 0x7a, 0xf5, 0x0f, 0xf6, 0x8a, 0xf7,
|
||||
0xbb, 0xf8, 0x93, 0xf9, 0x43, 0xfb, 0x55, 0xfd, 0xfb, 0xfe, 0xa3, 0x00,
|
||||
0xe9, 0x02, 0x2f, 0x05, 0x8f, 0x06, 0x1f, 0x08, 0xe4, 0x08, 0xb4, 0x09,
|
||||
0x96, 0x0a, 0xbc, 0x0b, 0x64, 0x0d, 0x2d, 0x0f, 0x23, 0x10, 0x72, 0x10,
|
||||
0x7b, 0x11, 0xee, 0x11, 0x16, 0x13, 0x35, 0x14, 0xfc, 0x13, 0x4f, 0x13,
|
||||
0x10, 0x13, 0xe2, 0x12, 0x73, 0x12, 0x45, 0x12, 0x74, 0x12, 0x4d, 0x12,
|
||||
0x01, 0x12, 0xc3, 0x10, 0x5d, 0x0f, 0xfd, 0x0e, 0x74, 0x0e, 0x55, 0x0d,
|
||||
0x17, 0x0b, 0x72, 0x09, 0x01, 0x09, 0x8d, 0x08, 0x8a, 0x08, 0x29, 0x07,
|
||||
0x05, 0x05, 0xbf, 0x03, 0xc5, 0x02, 0x02, 0x02, 0x33, 0x00, 0x67, 0xfd,
|
||||
0x2b, 0xfb, 0x09, 0xfa, 0xbd, 0xf8, 0x7f, 0xf7, 0x7a, 0xf5, 0x62, 0xf2,
|
||||
0x65, 0xf0, 0x64, 0xef, 0x80, 0xef, 0x98, 0xef, 0xed, 0xed, 0x18, 0xec,
|
||||
0x43, 0xeb, 0xe7, 0xeb, 0xc9, 0xed, 0xed, 0xed, 0x2a, 0xec, 0x16, 0xea,
|
||||
0x8a, 0xea, 0xec, 0xec, 0x4b, 0xec, 0x45, 0xed, 0x2b, 0xec, 0x62, 0xeb,
|
||||
0xdb, 0xee, 0xcc, 0xef, 0x6b, 0xf1, 0x53, 0xf1, 0xac, 0xf1, 0x7e, 0xf3,
|
||||
0x9c, 0xf4, 0x04, 0xf6, 0x5b, 0xf7, 0xd6, 0xf8, 0xfc, 0xf9, 0xfa, 0xfa,
|
||||
0xe8, 0xfc, 0x92, 0xff, 0x52, 0x02, 0x2e, 0x04, 0x07, 0x05, 0xaf, 0x06,
|
||||
0x1d, 0x08, 0x94, 0x0a, 0x3b, 0x0c, 0x21, 0x0c, 0x65, 0x0d, 0x28, 0x0f,
|
||||
0xbe, 0x0f, 0x6b, 0x11, 0xf0, 0x11, 0xfe, 0x11, 0x04, 0x14, 0xf8, 0x14,
|
||||
0xdf, 0x14, 0xde, 0x14, 0x2c, 0x14, 0xdd, 0x13, 0xb8, 0x14, 0x01, 0x14,
|
||||
0xe0, 0x12, 0x03, 0x12, 0xb7, 0x11, 0xa0, 0x11, 0x60, 0x11, 0x14, 0x10,
|
||||
0x43, 0x0e, 0x6f, 0x0d, 0x85, 0x0c, 0x89, 0x0b, 0x2d, 0x0a, 0x61, 0x08,
|
||||
0xea, 0x07, 0x4c, 0x07, 0x14, 0x06, 0xe3, 0x04, 0x7d, 0x03, 0x37, 0x02,
|
||||
0x0f, 0x00, 0xc6, 0xff, 0xba, 0xfd, 0x61, 0xfb, 0x69, 0xfa, 0x4f, 0xf8,
|
||||
0x4e, 0xf8, 0x70, 0xf6, 0x6f, 0xf3, 0xc0, 0xf1, 0x47, 0xef, 0x03, 0xef,
|
||||
0x1e, 0xef, 0xa1, 0xed, 0x55, 0xec, 0x8c, 0xeb, 0xa4, 0xec, 0xe9, 0xec,
|
||||
0x89, 0xed, 0xb0, 0xed, 0x23, 0xed, 0xa5, 0xee, 0xaa, 0xef, 0x46, 0xee,
|
||||
0x94, 0xeb, 0x0a, 0xeb, 0xc7, 0xec, 0xed, 0xef, 0x33, 0xf1, 0xb8, 0xef,
|
||||
0xfe, 0xee, 0xf6, 0xf0, 0x2e, 0xf4, 0xac, 0xf6, 0x14, 0xf7, 0xa6, 0xf6,
|
||||
0x5d, 0xf7, 0xba, 0xf8, 0x9c, 0xfa, 0x9e, 0xfb, 0xd6, 0xfc, 0x83, 0xff,
|
||||
0x88, 0x01, 0x1f, 0x03, 0x78, 0x05, 0xe3, 0x06, 0x75, 0x09, 0x26, 0x0b,
|
||||
0x99, 0x0b, 0x1c, 0x0d, 0xfa, 0x0d, 0xc1, 0x0f, 0xc5, 0x10, 0x58, 0x11,
|
||||
0xdd, 0x11, 0x08, 0x13, 0x41, 0x15, 0x4e, 0x16, 0x56, 0x16, 0xd7, 0x15,
|
||||
0xc6, 0x15, 0xda, 0x15, 0x69, 0x14, 0x9f, 0x13, 0x1f, 0x14, 0x6b, 0x13,
|
||||
0xc3, 0x12, 0xbd, 0x11, 0x81, 0x10, 0xf5, 0x10, 0xbe, 0x10, 0x7a, 0x0f,
|
||||
0x15, 0x0d, 0x45, 0x0c, 0x50, 0x0b, 0xbe, 0x09, 0x9c, 0x08, 0xa5, 0x06,
|
||||
0x28, 0x05, 0xcb, 0x03, 0x92, 0x03, 0x3d, 0x02, 0x3e, 0xff, 0xe9, 0xfc,
|
||||
0x75, 0xfb, 0xbb, 0xfa, 0xf1, 0xf8, 0x5d, 0xf6, 0xb1, 0xf3, 0xaa, 0xf1,
|
||||
0xeb, 0xf0, 0x56, 0xef, 0xa7, 0xec, 0x60, 0xea, 0x00, 0xe9, 0xfc, 0xe9,
|
||||
0xe8, 0xee, 0x84, 0xec, 0x46, 0xe8, 0x58, 0xe7, 0x35, 0xe9, 0xa7, 0xef,
|
||||
0x9a, 0xf1, 0x71, 0xec, 0x51, 0xe8, 0xe5, 0xe8, 0x3d, 0xec, 0x40, 0xf0,
|
||||
0xfd, 0xee, 0xc9, 0xea, 0x54, 0xed, 0x90, 0xf1, 0x65, 0xf6, 0xc9, 0xf6,
|
||||
0x2e, 0xf3, 0xd1, 0xf4, 0xf8, 0xf7, 0x3d, 0xfb, 0x9d, 0xfc, 0xe9, 0xfa,
|
||||
0x9c, 0xfb, 0xae, 0xfe, 0x14, 0x02, 0x02, 0x05, 0x13, 0x05, 0xc6, 0x06,
|
||||
0xf9, 0x08, 0x45, 0x0b, 0xc4, 0x0c, 0xdf, 0x0c, 0x18, 0x0e, 0x61, 0x10,
|
||||
0x16, 0x11, 0x58, 0x11, 0xae, 0x10, 0x54, 0x12, 0x1d, 0x15, 0x39, 0x16,
|
||||
0xb0, 0x15, 0xb9, 0x14, 0x30, 0x15, 0x2d, 0x16, 0xc8, 0x15, 0xce, 0x14,
|
||||
0x3f, 0x13, 0x08, 0x12, 0x00, 0x12, 0x50, 0x11, 0xaa, 0x10, 0x7f, 0x10,
|
||||
0xa2, 0x0f, 0x2f, 0x0f, 0xb6, 0x0d, 0x39, 0x0c, 0x4e, 0x0b, 0xfe, 0x09,
|
||||
0x27, 0x09, 0x8a, 0x07, 0x7e, 0x05, 0x0b, 0x04, 0x90, 0x03, 0xf7, 0x02,
|
||||
0x34, 0x01, 0xca, 0xfe, 0x8d, 0xfd, 0x68, 0xfc, 0xb6, 0xfb, 0x25, 0xf9,
|
||||
0x10, 0xf6, 0x14, 0xf4, 0xf5, 0xf1, 0x6a, 0xf0, 0x31, 0xee, 0xe9, 0xeb,
|
||||
0x41, 0xea, 0xe8, 0xea, 0xd6, 0xea, 0xc4, 0xea, 0x24, 0xea, 0xaa, 0xe9,
|
||||
0x44, 0xeb, 0x84, 0xeb, 0xe7, 0xec, 0xc8, 0xed, 0xe5, 0xeb, 0xa1, 0xeb,
|
||||
0x0f, 0xeb, 0x06, 0xeb, 0xd2, 0xec, 0x8a, 0xee, 0x43, 0xef, 0xe1, 0xef,
|
||||
0x59, 0xf1, 0xdf, 0xf2, 0xaa, 0xf5, 0xe9, 0xf6, 0xa5, 0xf6, 0xc2, 0xf7,
|
||||
0x30, 0xf9, 0x1b, 0xfb, 0x08, 0xfd, 0x95, 0xfd, 0x22, 0xfe, 0x1d, 0x00,
|
||||
0x4c, 0x04, 0x76, 0x06, 0x67, 0x07, 0xcb, 0x08, 0x8b, 0x0a, 0x79, 0x0c,
|
||||
0x42, 0x0d, 0xfe, 0x0d, 0x99, 0x0e, 0xab, 0x0f, 0x43, 0x11, 0x6e, 0x11,
|
||||
0xb3, 0x10, 0xea, 0x12, 0x1f, 0x15, 0xb5, 0x15, 0xd3, 0x15, 0x8d, 0x14,
|
||||
0x2a, 0x15, 0xa1, 0x15, 0x11, 0x15, 0x54, 0x14, 0x35, 0x13, 0xb9, 0x12,
|
||||
0xec, 0x12, 0xcd, 0x12, 0x5b, 0x11, 0x8f, 0x10, 0xf5, 0x0f, 0x89, 0x0e,
|
||||
0x29, 0x0e, 0x13, 0x0d, 0xad, 0x0b, 0x6a, 0x0a, 0x3e, 0x09, 0xab, 0x07,
|
||||
0xc9, 0x05, 0xc9, 0x04, 0xc7, 0x03, 0x0a, 0x02, 0xa0, 0x01, 0xf1, 0xfe,
|
||||
0xe7, 0xfa, 0xff, 0xf9, 0xb2, 0xf8, 0x43, 0xf7, 0x7f, 0xf5, 0x1d, 0xf1,
|
||||
0xbc, 0xee, 0x4a, 0xed, 0x18, 0xec, 0x2f, 0xe9, 0xdb, 0xe8, 0x00, 0xe8,
|
||||
0xd1, 0xe8, 0xb7, 0xe9, 0x5f, 0xe7, 0x64, 0xe7, 0x65, 0xe8, 0xa7, 0xea,
|
||||
0xc2, 0xeb, 0xd2, 0xea, 0xfc, 0xe8, 0x76, 0xe8, 0x7a, 0xea, 0x65, 0xec,
|
||||
0x29, 0xed, 0x82, 0xed, 0x36, 0xee, 0xff, 0xef, 0xbb, 0xf2, 0xc8, 0xf4,
|
||||
0xe3, 0xf5, 0x3d, 0xf6, 0xe1, 0xf7, 0x25, 0xfb, 0x80, 0xfc, 0xc1, 0xfc,
|
||||
0x09, 0xfd, 0x0f, 0xff, 0xc0, 0x01, 0x3f, 0x05, 0xb4, 0x06, 0xa2, 0x07,
|
||||
0xc0, 0x08, 0xa1, 0x0b, 0xb7, 0x0d, 0xf6, 0x0f, 0x16, 0x10, 0xf8, 0x0e,
|
||||
0x42, 0x10, 0xb8, 0x11, 0xbe, 0x13, 0xbb, 0x14, 0x71, 0x14, 0x07, 0x15,
|
||||
0xfa, 0x15, 0x1f, 0x16, 0x0a, 0x16, 0xf6, 0x15, 0x3e, 0x17, 0x04, 0x17,
|
||||
0x1f, 0x15, 0x34, 0x13, 0x34, 0x12, 0xe8, 0x12, 0x9f, 0x12, 0x08, 0x12,
|
||||
0x85, 0x10, 0x28, 0x0e, 0xa4, 0x0d, 0x2b, 0x0d, 0x25, 0x0c, 0xc1, 0x0a,
|
||||
0xed, 0x08, 0xa9, 0x07, 0xab, 0x06, 0xed, 0x04, 0xbe, 0x02, 0x3d, 0x01,
|
||||
0x40, 0x00, 0x87, 0xfe, 0x9b, 0xfc, 0x5f, 0xfa, 0x73, 0xf7, 0x56, 0xf6,
|
||||
0xa5, 0xf5, 0x6c, 0xf3, 0x3b, 0xf0, 0xc1, 0xec, 0x53, 0xea, 0x3f, 0xea,
|
||||
0xfb, 0xe8, 0x1c, 0xe8, 0x79, 0xe7, 0xed, 0xe6, 0xf5, 0xe5, 0xde, 0xe6,
|
||||
0x47, 0xe9, 0x79, 0xeb, 0xff, 0xec, 0x14, 0xec, 0xf2, 0xe9, 0x25, 0xe9,
|
||||
0xa3, 0xea, 0xf3, 0xec, 0x36, 0xee, 0x28, 0xee, 0x8b, 0xee, 0xf2, 0xee,
|
||||
0x4f, 0xf2, 0x2d, 0xf5, 0x7e, 0xf7, 0x4c, 0xf9, 0x53, 0xfa, 0x02, 0xfc,
|
||||
0x3c, 0xfc, 0xf9, 0xfb, 0x98, 0xfe, 0x88, 0x00, 0xf5, 0x02, 0x04, 0x05,
|
||||
0x99, 0x05, 0x50, 0x07, 0xe3, 0x09, 0x62, 0x0d, 0xa0, 0x0e, 0xf4, 0x0e,
|
||||
0x9b, 0x0f, 0x1f, 0x11, 0x50, 0x12, 0x2b, 0x12, 0x6b, 0x11, 0xb6, 0x11,
|
||||
0xc2, 0x13, 0x45, 0x15, 0x8a, 0x15, 0x57, 0x15, 0x35, 0x15, 0x1c, 0x16,
|
||||
0x84, 0x16, 0x12, 0x15, 0xf0, 0x12, 0xf7, 0x11, 0x54, 0x12, 0x49, 0x12,
|
||||
0x78, 0x11, 0xd4, 0x0f, 0x4f, 0x0e, 0xcb, 0x0d, 0xc7, 0x0d, 0x35, 0x0d,
|
||||
0x3b, 0x0c, 0x73, 0x0b, 0x9c, 0x09, 0xe5, 0x06, 0x16, 0x05, 0xea, 0x03,
|
||||
0x68, 0x03, 0xa9, 0x02, 0x55, 0x00, 0x80, 0xfd, 0x20, 0xfb, 0xf6, 0xf8,
|
||||
0xff, 0xf8, 0xcd, 0xf7, 0x6a, 0xf5, 0x46, 0xf2, 0xcd, 0xee, 0x3e, 0xec,
|
||||
0x49, 0xea, 0xd4, 0xe8, 0x99, 0xe7, 0x82, 0xe5, 0xd9, 0xe5, 0x48, 0xe6,
|
||||
0x35, 0xe6, 0x30, 0xe8, 0xd4, 0xe7, 0xbf, 0xe8, 0xe4, 0xe9, 0xd7, 0xea,
|
||||
0x72, 0xeb, 0x6f, 0xea, 0x5b, 0xe9, 0xed, 0xe9, 0x74, 0xeb, 0xfa, 0xed,
|
||||
0xc4, 0xef, 0x91, 0xf0, 0x66, 0xf2, 0xa4, 0xf4, 0x07, 0xf7, 0xaf, 0xfb,
|
||||
0x2b, 0xfd, 0xf6, 0xfc, 0x9f, 0xfe, 0x65, 0xff, 0xcc, 0xff, 0x51, 0x02,
|
||||
0x3d, 0x04, 0x18, 0x06, 0xea, 0x08, 0x1c, 0x0a, 0x44, 0x0b, 0xdb, 0x0d,
|
||||
0x1b, 0x10, 0x76, 0x11, 0xcb, 0x12, 0x69, 0x12, 0xac, 0x11, 0x73, 0x12,
|
||||
0x1f, 0x13, 0x78, 0x13, 0x2b, 0x15, 0x08, 0x15, 0xd4, 0x14, 0xb1, 0x14,
|
||||
0xbe, 0x14, 0x15, 0x16, 0x19, 0x16, 0x0d, 0x14, 0xc1, 0x12, 0x9d, 0x11,
|
||||
0xaf, 0x11, 0xd3, 0x10, 0x15, 0x10, 0xd8, 0x0e, 0x75, 0x0d, 0xa3, 0x0d,
|
||||
0xeb, 0x0c, 0x90, 0x0b, 0x56, 0x0a, 0x34, 0x09, 0x6e, 0x08, 0x14, 0x07,
|
||||
0x87, 0x04, 0x27, 0x02, 0xed, 0x00, 0xcd, 0xff, 0x46, 0xfe, 0x7f, 0xfc,
|
||||
0xae, 0xf9, 0x13, 0xf7, 0x2f, 0xf7, 0xf1, 0xf5, 0x27, 0xf3, 0xe5, 0xef,
|
||||
0x2c, 0xec, 0x52, 0xea, 0x8f, 0xe9, 0x30, 0xe7, 0x56, 0xe5, 0x16, 0xe5,
|
||||
0x45, 0xe5, 0xbf, 0xe6, 0xf9, 0xe6, 0x96, 0xe6, 0x8e, 0xe8, 0x51, 0xec,
|
||||
0xed, 0xec, 0x1d, 0xeb, 0xdd, 0xe8, 0x4c, 0xe9, 0xfb, 0xec, 0x8f, 0xee,
|
||||
0x0b, 0xee, 0xca, 0xed, 0xfc, 0xef, 0x97, 0xf4, 0xc2, 0xf8, 0x21, 0xf9,
|
||||
0xb9, 0xf8, 0xe8, 0xfa, 0x0f, 0x00, 0x90, 0x03, 0x9a, 0x01, 0xfd, 0xff,
|
||||
0xeb, 0x00, 0xd4, 0x03, 0x8e, 0x07, 0xda, 0x08, 0xa0, 0x08, 0xc1, 0x0a,
|
||||
0xfc, 0x0d, 0x50, 0x10, 0x82, 0x12, 0x43, 0x12, 0xcf, 0x10, 0xdb, 0x11,
|
||||
0x80, 0x11, 0x41, 0x12, 0xe2, 0x12, 0x8d, 0x12, 0x2c, 0x13, 0xe6, 0x13,
|
||||
0x25, 0x14, 0x2d, 0x14, 0x7b, 0x14, 0xb2, 0x14, 0x29, 0x14, 0x60, 0x13,
|
||||
0xb7, 0x11, 0xf1, 0x10, 0x4d, 0x11, 0x57, 0x10, 0xe0, 0x0e, 0x3c, 0x0e,
|
||||
0x21, 0x0e, 0x23, 0x0d, 0x8c, 0x0c, 0xf8, 0x0b, 0x20, 0x0b, 0xf3, 0x09,
|
||||
0xd7, 0x07, 0xd8, 0x04, 0xf2, 0x02, 0x8b, 0x01, 0x94, 0xff, 0xa4, 0xfd,
|
||||
0xc3, 0xfa, 0x15, 0xf9, 0x9d, 0xf8, 0x51, 0xf7, 0x07, 0xf5, 0xd6, 0xf1,
|
||||
0x3f, 0xee, 0xce, 0xeb, 0x36, 0xe9, 0x51, 0xe6, 0x4c, 0xe4, 0x1e, 0xe4,
|
||||
0x56, 0xe2, 0x03, 0xe2, 0x40, 0xe3, 0xde, 0xe3, 0x29, 0xe7, 0x20, 0xeb,
|
||||
0xa4, 0xea, 0x51, 0xea, 0x99, 0xea, 0xba, 0xe9, 0x56, 0xec, 0x92, 0xed,
|
||||
0x11, 0xec, 0x0f, 0xee, 0x43, 0xf1, 0x96, 0xf3, 0x23, 0xf6, 0xd3, 0xf7,
|
||||
0xd9, 0xf9, 0x86, 0xfd, 0x31, 0x00, 0xd9, 0x01, 0xda, 0x03, 0xc7, 0x04,
|
||||
0x2f, 0x05, 0xb0, 0x05, 0x99, 0x06, 0xef, 0x08, 0x4e, 0x0c, 0x17, 0x0e,
|
||||
0x4e, 0x0f, 0xa9, 0x0f, 0x32, 0x10, 0xc0, 0x11, 0x42, 0x13, 0x10, 0x14,
|
||||
0xfa, 0x12, 0x9b, 0x11, 0x88, 0x10, 0xb3, 0x10, 0xb0, 0x11, 0xe4, 0x12,
|
||||
0x0c, 0x13, 0xcb, 0x12, 0xdb, 0x11, 0xd0, 0x11, 0x15, 0x13, 0xb9, 0x12,
|
||||
0x1c, 0x12, 0x2e, 0x11, 0x37, 0x10, 0x5f, 0x0f, 0x85, 0x0e, 0xe0, 0x0d,
|
||||
0x30, 0x0e, 0xff, 0x0d, 0xe4, 0x0d, 0x5a, 0x0c, 0x12, 0x0b, 0x09, 0x0a,
|
||||
0x2c, 0x09, 0x2b, 0x08, 0xac, 0x05, 0xe2, 0x02, 0x51, 0x00, 0x0f, 0xfe,
|
||||
0xb1, 0xfc, 0x1e, 0xfb, 0x95, 0xf8, 0xd4, 0xf5, 0x1d, 0xf3, 0xaf, 0xf0,
|
||||
0xaa, 0xed, 0x78, 0xea, 0x98, 0xe6, 0xa9, 0xe4, 0x0f, 0xe4, 0x41, 0xe3,
|
||||
0x98, 0xe3, 0xf5, 0xe1, 0xd2, 0xe2, 0x21, 0xe6, 0x2b, 0xe7, 0x71, 0xe8,
|
||||
0x8c, 0xe9, 0x4a, 0xea, 0xe6, 0xeb, 0xeb, 0xeb, 0xe8, 0xeb, 0xfb, 0xea,
|
||||
0xf0, 0xea, 0x6a, 0xed, 0x96, 0xef, 0xb5, 0xf4, 0xaf, 0xf7, 0x0c, 0xf9,
|
||||
0xd7, 0xfc, 0x87, 0xfd, 0x2f, 0x00, 0x6e, 0x02, 0x30, 0x03, 0x6c, 0x04,
|
||||
0xcf, 0x05, 0x9b, 0x05, 0xbc, 0x07, 0xae, 0x09, 0xc4, 0x0b, 0xd3, 0x0e,
|
||||
0x1e, 0x10, 0xc0, 0x11, 0x8f, 0x12, 0x2d, 0x12, 0x54, 0x13, 0xcf, 0x12,
|
||||
0x1a, 0x12, 0xee, 0x10, 0x70, 0x0f, 0x42, 0x10, 0xe7, 0x10, 0x99, 0x11,
|
||||
0x8a, 0x11, 0x26, 0x12, 0xb4, 0x12, 0x71, 0x13, 0xaa, 0x12, 0xd9, 0x11,
|
||||
0x19, 0x10, 0x4d, 0x10, 0x84, 0x10, 0xc1, 0x0f, 0x61, 0x0f, 0xc9, 0x0e,
|
||||
0xaf, 0x0e, 0x41, 0x0f, 0x66, 0x0f, 0xc7, 0x0e, 0x12, 0x0e, 0x1d, 0x0c,
|
||||
0xc3, 0x09, 0x93, 0x07, 0x5c, 0x06, 0x68, 0x04, 0x6e, 0x01, 0x6b, 0xfd,
|
||||
0x1d, 0xfa, 0x52, 0xfa, 0x39, 0xf8, 0x3e, 0xf5, 0x19, 0xf1, 0x19, 0xee,
|
||||
0x0d, 0xeb, 0x14, 0xe8, 0x4e, 0xe5, 0xc2, 0xe2, 0x9c, 0xe2, 0xfc, 0xe2,
|
||||
0x7f, 0xe2, 0x02, 0xe3, 0x41, 0xe4, 0x31, 0xe8, 0xeb, 0xea, 0x21, 0xeb,
|
||||
0xd0, 0xe9, 0xf5, 0xe8, 0xf7, 0xe9, 0x8b, 0xed, 0x94, 0xed, 0x61, 0xec,
|
||||
0x75, 0xed, 0x5c, 0xef, 0x27, 0xf4, 0xf2, 0xf6, 0xd2, 0xf8, 0x8f, 0xfb,
|
||||
0x6b, 0xfe, 0x1d, 0x01, 0x42, 0x02, 0x4c, 0x02, 0x7c, 0x03, 0x3f, 0x04,
|
||||
0xb6, 0x05, 0x0e, 0x07, 0xc9, 0x07, 0xef, 0x09, 0x6e, 0x0c, 0xd7, 0x0e,
|
||||
0x21, 0x10, 0xf1, 0x0f, 0x23, 0x10, 0x37, 0x10, 0xc6, 0x11, 0xcd, 0x10,
|
||||
0x3d, 0x0f, 0xf0, 0x0e, 0xda, 0x0e, 0x24, 0x0f, 0xb6, 0x0f, 0x30, 0x10,
|
||||
0x2d, 0x0f, 0x6d, 0x10, 0x60, 0x11, 0x04, 0x11, 0xa1, 0x10, 0xd9, 0x0f,
|
||||
0x0b, 0x10, 0x4e, 0x10, 0x6d, 0x10, 0xdc, 0x10, 0x96, 0x10, 0x9a, 0x10,
|
||||
0xfb, 0x11, 0x11, 0x12, 0xc7, 0x11, 0x0b, 0x11, 0x56, 0x0f, 0xa4, 0x0c,
|
||||
0x8d, 0x0a, 0x8a, 0x07, 0xaf, 0x04, 0xc4, 0x00, 0x7b, 0xfd, 0x25, 0xfb,
|
||||
0x97, 0xf8, 0x93, 0xf5, 0xe3, 0xf2, 0x2c, 0xef, 0xcc, 0xea, 0x98, 0xe6,
|
||||
0xb3, 0xe3, 0xea, 0xe2, 0x0b, 0xe3, 0xb6, 0xe2, 0x56, 0xe1, 0xfe, 0xdf,
|
||||
0xf6, 0xe1, 0x1c, 0xe5, 0x62, 0xe8, 0x4d, 0xe9, 0x4d, 0xe8, 0x4f, 0xe8,
|
||||
0x64, 0xe9, 0xcf, 0xeb, 0x17, 0xed, 0x13, 0xee, 0xcf, 0xec, 0x0b, 0xee,
|
||||
0xd0, 0xf2, 0xca, 0xf6, 0x9f, 0xf9, 0x6f, 0xfc, 0x14, 0xff, 0x46, 0x02,
|
||||
0x08, 0x04, 0x72, 0x02, 0x21, 0x04, 0x1d, 0x05, 0xb1, 0x06, 0xd4, 0x09,
|
||||
0x75, 0x09, 0x9a, 0x09, 0xe3, 0x0a, 0x12, 0x0d, 0x34, 0x0f, 0x26, 0x0f,
|
||||
0xe5, 0x0e, 0x59, 0x0f, 0xdc, 0x0f, 0x8d, 0x0e, 0x2c, 0x0c, 0xce, 0x0a,
|
||||
0x2b, 0x0b, 0x63, 0x0d, 0x5a, 0x0e, 0x93, 0x0d, 0x35, 0x0c, 0x60, 0x0d,
|
||||
0xa0, 0x0f, 0xbe, 0x10, 0xd8, 0x0e, 0xfa, 0x0c, 0x34, 0x0d, 0xa3, 0x0e,
|
||||
0x49, 0x0f, 0x98, 0x0e, 0xa2, 0x0d, 0x3b, 0x0f, 0xc5, 0x11, 0xcd, 0x12,
|
||||
0x45, 0x12, 0x70, 0x10, 0x08, 0x0f, 0x87, 0x0e, 0x17, 0x0d, 0x26, 0x0a,
|
||||
0x57, 0x05, 0xf5, 0xff, 0x1b, 0xfe, 0xa4, 0xfc, 0xef, 0xfa, 0x5f, 0xf6,
|
||||
0xf6, 0xf1, 0xae, 0xee, 0x0d, 0xea, 0x02, 0xe8, 0x03, 0xe6, 0x9d, 0xe5,
|
||||
0xd3, 0xe5, 0xf9, 0xe4, 0x04, 0xe5, 0x7e, 0xe6, 0x29, 0xe7, 0x51, 0xe9,
|
||||
0x73, 0xe9, 0x00, 0xeb, 0xad, 0xeb, 0xf8, 0xe8, 0x59, 0xe8, 0x07, 0xe8,
|
||||
0x23, 0xeb, 0x63, 0xef, 0x44, 0xf1, 0x52, 0xf1, 0xbe, 0xf2, 0xed, 0xf6,
|
||||
0x22, 0xfb, 0x2c, 0xff, 0xcf, 0xfe, 0xf6, 0xfe, 0x15, 0x01, 0xaf, 0x02,
|
||||
0xf1, 0x03, 0xe8, 0x04, 0x87, 0x04, 0x6e, 0x06, 0xb1, 0x08, 0x56, 0x09,
|
||||
0xfe, 0x0a, 0x71, 0x09, 0x2c, 0x0a, 0xb5, 0x0b, 0x37, 0x0c, 0x18, 0x0c,
|
||||
0xb5, 0x0a, 0x4e, 0x0a, 0x61, 0x0a, 0x72, 0x0a, 0x9e, 0x09, 0x15, 0x09,
|
||||
0x2c, 0x0a, 0x8b, 0x0c, 0x46, 0x0e, 0xfc, 0x0c, 0xed, 0x0a, 0xc8, 0x0a,
|
||||
0x01, 0x0e, 0x99, 0x0f, 0x2f, 0x0f, 0x97, 0x0c, 0x9d, 0x0c, 0x20, 0x0f,
|
||||
0xbb, 0x11, 0xbe, 0x12, 0x4b, 0x12, 0xe5, 0x11, 0x9b, 0x12, 0x7f, 0x13,
|
||||
0x56, 0x11, 0x84, 0x0c, 0xd1, 0x08, 0x6a, 0x08, 0xd9, 0x05, 0x72, 0x01,
|
||||
0xc5, 0xfc, 0x1f, 0xf9, 0xe9, 0xf9, 0xf5, 0xf6, 0xa0, 0xf0, 0x27, 0xeb,
|
||||
0xcf, 0xe5, 0x11, 0xe6, 0x27, 0xea, 0x4b, 0xe9, 0xc2, 0xe7, 0x49, 0xe7,
|
||||
0xd2, 0xe5, 0x7b, 0xe9, 0x65, 0xeb, 0xb1, 0xe8, 0xde, 0xe7, 0x33, 0xe9,
|
||||
0x1e, 0xea, 0x7e, 0xec, 0x60, 0xed, 0x21, 0xea, 0x25, 0xee, 0xad, 0xf1,
|
||||
0x27, 0xf5, 0x4b, 0xf9, 0xf1, 0xf9, 0xa6, 0xfc, 0x6a, 0x00, 0x3e, 0x00,
|
||||
0x9f, 0x01, 0x5e, 0x02, 0xac, 0x03, 0xa5, 0x05, 0x6d, 0x05, 0xb9, 0x04,
|
||||
0xfd, 0x05, 0xd3, 0x07, 0xcd, 0x08, 0xbf, 0x09, 0x34, 0x09, 0x42, 0x09,
|
||||
0xda, 0x07, 0x4f, 0x08, 0x35, 0x09, 0xd5, 0x08, 0x83, 0x08, 0xfe, 0x08,
|
||||
0xcc, 0x07, 0x5b, 0x07, 0x58, 0x07, 0x6f, 0x08, 0x83, 0x0a, 0xd1, 0x0a,
|
||||
0x20, 0x09, 0x1e, 0x0b, 0x1c, 0x0d, 0x16, 0x0d, 0x62, 0x0e, 0x71, 0x0e,
|
||||
0xa1, 0x0d, 0xdd, 0x0f, 0x69, 0x11, 0x4a, 0x14, 0x03, 0x15, 0xbc, 0x13,
|
||||
0xb2, 0x12, 0x15, 0x12, 0x9a, 0x10, 0x04, 0x0f, 0x0e, 0x0c, 0x0b, 0x06,
|
||||
0x3b, 0x02, 0x7f, 0xfe, 0x85, 0xfb, 0x4e, 0xf8, 0xc4, 0xf4, 0xe0, 0xed,
|
||||
0x09, 0xe8, 0x75, 0xe4, 0xb4, 0xe2, 0x62, 0xe5, 0x8c, 0xe8, 0x98, 0xe7,
|
||||
0xe3, 0xe5, 0x03, 0xe8, 0x9e, 0xea, 0xc6, 0xf0, 0xf6, 0xf2, 0x95, 0xf1,
|
||||
0xad, 0xef, 0x91, 0xf0, 0xbe, 0xf1, 0x58, 0xf4, 0x02, 0xf5, 0x1d, 0xf6,
|
||||
0xb0, 0xfa, 0xfa, 0xfe, 0x10, 0x04, 0x95, 0x06, 0x0f, 0x09, 0xf4, 0x0a,
|
||||
0xd6, 0x0b, 0xcb, 0x0b, 0x91, 0x09, 0xf6, 0x07, 0xc8, 0x06, 0xeb, 0x07,
|
||||
0xeb, 0x06, 0xb6, 0x03, 0xde, 0x01, 0x84, 0xff, 0x4d, 0x01, 0xc5, 0x02,
|
||||
0x03, 0x01, 0x2e, 0xfe, 0x1e, 0xfb, 0x01, 0xfa, 0xc1, 0xf9, 0xb1, 0xfa,
|
||||
0xd2, 0xf8, 0x03, 0xf8, 0x76, 0xf9, 0x25, 0xfb, 0x31, 0xfe, 0xa9, 0x01,
|
||||
0xea, 0x03, 0x19, 0x07, 0xe6, 0x08, 0xf9, 0x0a, 0x8c, 0x0c, 0x50, 0x0e,
|
||||
0x52, 0x10, 0xe4, 0x12, 0x0f, 0x15, 0xa1, 0x16, 0x19, 0x1a, 0xf2, 0x1b,
|
||||
0x3d, 0x1d, 0x2f, 0x1d, 0xd4, 0x19, 0xa5, 0x15, 0xad, 0x11, 0x2c, 0x0d,
|
||||
0x71, 0x08, 0x3e, 0x05, 0x7f, 0x01, 0xb4, 0xfb, 0xf3, 0xf6, 0x38, 0xed,
|
||||
0x46, 0xe8, 0x22, 0xe2, 0x9d, 0xdf, 0x01, 0xdd, 0x06, 0xdc, 0x08, 0xdc,
|
||||
0xe9, 0xdd, 0xe2, 0xe1, 0x7c, 0xe8, 0xf7, 0xee, 0xb5, 0xf4, 0x42, 0xf7,
|
||||
0xf9, 0xf7, 0x34, 0xfb, 0xd5, 0xfc, 0x9b, 0xff, 0xa8, 0x01, 0x5f, 0x02,
|
||||
0xe6, 0x02, 0x35, 0x05, 0xe2, 0x08, 0x42, 0x0e, 0xf1, 0x14, 0x1d, 0x17,
|
||||
0xd0, 0x16, 0x27, 0x13, 0x62, 0x0e, 0xe2, 0x08, 0xa5, 0x03, 0xc6, 0xfe,
|
||||
0x7f, 0xf9, 0x63, 0xf7, 0xe5, 0xf5, 0x23, 0xf5, 0xdb, 0xf3, 0xcf, 0xf2,
|
||||
0x45, 0xf1, 0x44, 0xf0, 0x36, 0xf0, 0x63, 0xee, 0xaf, 0xec, 0x50, 0xe8,
|
||||
0x4b, 0xe7, 0xa7, 0xe8, 0xff, 0xec, 0xff, 0xf3, 0xca, 0xf7, 0x01, 0x00,
|
||||
0x75, 0x07, 0xd9, 0x0e, 0x43, 0x15, 0x22, 0x19, 0x89, 0x1d, 0xa6, 0x20,
|
||||
0x44, 0x22, 0x45, 0x24, 0x5e, 0x26, 0x83, 0x29, 0x4a, 0x2b, 0xbc, 0x2b,
|
||||
0xba, 0x29, 0x84, 0x25, 0x57, 0x20, 0x6d, 0x18, 0x73, 0x11, 0xad, 0x08,
|
||||
0x4d, 0xfd, 0xa3, 0xf7, 0xa7, 0xf0, 0x98, 0xed, 0x85, 0xea, 0x54, 0xe4,
|
||||
0xa5, 0xdd, 0x29, 0xd4, 0x9d, 0xcb, 0xc2, 0xc4, 0x9f, 0xc5, 0x4c, 0xc8,
|
||||
0x69, 0xd0, 0x13, 0xd7, 0x36, 0xdd, 0x3d, 0xe9, 0x62, 0xf5, 0xf1, 0x02,
|
||||
0x07, 0x0c, 0x37, 0x11, 0x3f, 0x17, 0xb6, 0x1b, 0x73, 0x1f, 0x91, 0x20,
|
||||
0x45, 0x20, 0x61, 0x22, 0xcf, 0x23, 0x3f, 0x26, 0xa5, 0x26, 0x33, 0x27,
|
||||
0xd9, 0x1e, 0xb8, 0x15, 0x25, 0x0b, 0x14, 0xfe, 0xeb, 0xf3, 0x80, 0xec,
|
||||
0xdf, 0xe0, 0x6b, 0xd8, 0x45, 0xd6, 0x52, 0xd3, 0x55, 0xd7, 0x2d, 0xd8,
|
||||
0xab, 0xd5, 0x7b, 0xd7, 0x5c, 0xda, 0x76, 0xdf, 0xd4, 0xe3, 0xec, 0xe2,
|
||||
0x38, 0xe8, 0x94, 0xed, 0x00, 0xf8, 0xf0, 0x02, 0x62, 0x0b, 0x80, 0x16,
|
||||
0xfb, 0x1f, 0x9a, 0x28, 0xc0, 0x30, 0x64, 0x34, 0x0e, 0x32, 0x46, 0x33,
|
||||
0x8d, 0x33, 0x66, 0x35, 0xe8, 0x32, 0x50, 0x2f, 0x78, 0x2a, 0xfa, 0x26,
|
||||
0xc7, 0x20, 0xf3, 0x19, 0x30, 0x11, 0xb9, 0x09, 0x92, 0x02, 0xb1, 0xfa,
|
||||
0x80, 0xee, 0x49, 0xe3, 0xbe, 0xdd, 0x62, 0xd8, 0x21, 0xd5, 0xc3, 0xcc,
|
||||
0xf9, 0xc2, 0x3e, 0xb5, 0xdf, 0xb0, 0xb7, 0xae, 0x95, 0xb4, 0xa8, 0xc5,
|
||||
0x6f, 0xd4, 0x19, 0xe9, 0xa5, 0xfc, 0xc6, 0x10, 0x6d, 0x23, 0xfa, 0x32,
|
||||
0x5c, 0x3b, 0x69, 0x3d, 0xac, 0x3f, 0x62, 0x3b, 0x95, 0x39, 0xb9, 0x32,
|
||||
0xff, 0x2e, 0xb1, 0x2b, 0x60, 0x28, 0xb1, 0x23, 0x24, 0x1c, 0xef, 0x0f,
|
||||
0x29, 0xff, 0x3a, 0xef, 0x03, 0xdf, 0x7f, 0xd2, 0xab, 0xca, 0xf5, 0xc5,
|
||||
0x0f, 0xc3, 0xb0, 0xc3, 0xb8, 0xc4, 0x71, 0xc8, 0xb2, 0xcd, 0x24, 0xd3,
|
||||
0xdb, 0xdc, 0x59, 0xe5, 0xd0, 0xeb, 0xb8, 0xf1, 0x0a, 0xf8, 0xbb, 0x00,
|
||||
0x58, 0x0c, 0x3e, 0x17, 0x42, 0x1e, 0xdc, 0x25, 0x6d, 0x2b, 0xe9, 0x2f,
|
||||
0xa2, 0x33, 0xa6, 0x32, 0x54, 0x33, 0xa8, 0x31, 0x7d, 0x2d, 0xd7, 0x28,
|
||||
0xe7, 0x23, 0x8d, 0x1e, 0x00, 0x1b, 0x38, 0x16, 0x4c, 0x0d, 0xa9, 0x05,
|
||||
0xb6, 0xff, 0x2e, 0xfb, 0x75, 0xf5, 0x4e, 0xef, 0x68, 0xea, 0xf0, 0xe5,
|
||||
0x29, 0xe2, 0xa9, 0xdc, 0x44, 0xd6, 0xd8, 0xd0, 0x2e, 0xcc, 0x82, 0xc5,
|
||||
0xb6, 0xbe, 0x15, 0xb8, 0x98, 0xb8, 0xd2, 0xc4, 0x84, 0xd7, 0xc5, 0xeb,
|
||||
0xfa, 0xff, 0x15, 0x15, 0x7c, 0x28, 0x37, 0x3c, 0x60, 0x46, 0x79, 0x45,
|
||||
0x3c, 0x43, 0x37, 0x3e, 0xf5, 0x3a, 0xf3, 0x36, 0x24, 0x2d, 0xed, 0x24,
|
||||
0xcb, 0x1e, 0x3c, 0x19, 0x61, 0x0f, 0xbd, 0x03, 0x31, 0xf5, 0xb2, 0xe8,
|
||||
0x11, 0xdd, 0x58, 0xd3, 0x62, 0xcb, 0xe0, 0xc6, 0xc3, 0xc3, 0x00, 0xc2,
|
||||
0xd3, 0xc4, 0xb2, 0xc9, 0x2a, 0xd2, 0xe6, 0xd9, 0xf2, 0xe3, 0x75, 0xec,
|
||||
0x4a, 0xf6, 0xf8, 0xfe, 0x49, 0x06, 0x5c, 0x0c, 0xf2, 0x10, 0x6e, 0x18,
|
||||
0x24, 0x1f, 0x81, 0x25, 0xff, 0x28, 0xba, 0x2a, 0x67, 0x2c, 0xe1, 0x2d,
|
||||
0x97, 0x27, 0x14, 0x21, 0xf8, 0x1b, 0x53, 0x18, 0xdf, 0x19, 0x62, 0x16,
|
||||
0xe4, 0x11, 0x77, 0x0e, 0x61, 0x09, 0x89, 0x05, 0xed, 0x02, 0x7c, 0xfe,
|
||||
0x57, 0xf9, 0x48, 0xf7, 0xa9, 0xf2, 0xec, 0xed, 0xca, 0xe9, 0x51, 0xe5,
|
||||
0x0f, 0xe0, 0x99, 0xda, 0x12, 0xd3, 0xb0, 0xcb, 0x44, 0xc5, 0x46, 0xc0,
|
||||
0x91, 0xbd, 0xe9, 0xbf, 0x97, 0xcb, 0xa0, 0xdf, 0x56, 0xf9, 0x1e, 0x13,
|
||||
0x35, 0x22, 0xf2, 0x32, 0xd0, 0x3d, 0xc4, 0x45, 0x55, 0x49, 0x99, 0x43,
|
||||
0x34, 0x3d, 0x9b, 0x35, 0x06, 0x2f, 0xe1, 0x26, 0x5b, 0x23, 0x70, 0x19,
|
||||
0x44, 0x10, 0xf4, 0x04, 0x6d, 0xf7, 0x46, 0xe5, 0x9a, 0xd7, 0xf7, 0xcd,
|
||||
0xde, 0xc9, 0x45, 0xc6, 0x42, 0xc2, 0x13, 0xc4, 0x27, 0xc9, 0x87, 0xd3,
|
||||
0x06, 0xdc, 0x42, 0xe4, 0x0e, 0xed, 0xf7, 0xf5, 0xf9, 0xfd, 0xae, 0x05,
|
||||
0x7e, 0x0a, 0x1f, 0x0f, 0xad, 0x16, 0x55, 0x1c, 0xec, 0x20, 0xa3, 0x22,
|
||||
0x74, 0x21, 0x58, 0x21, 0x9f, 0x20, 0x02, 0x22, 0x59, 0x1e, 0xe9, 0x17,
|
||||
0x5f, 0x12, 0x5b, 0x0d, 0x47, 0x0d, 0xb2, 0x0d, 0xb6, 0x0d, 0xfc, 0x0c,
|
||||
0xde, 0x0c, 0x00, 0x0a, 0x6f, 0x04, 0x7e, 0x01, 0x66, 0xfd, 0xb6, 0xfa,
|
||||
0x36, 0xf7, 0xb0, 0xf0, 0x90, 0xeb, 0xf2, 0xe3, 0x9d, 0xdd, 0x85, 0xd9,
|
||||
0x15, 0xcf, 0x33, 0xc9, 0x0a, 0xc2, 0x5f, 0xbc, 0x26, 0xbc, 0x4b, 0xc5,
|
||||
0xdd, 0xd4, 0xe2, 0xf0, 0xfe, 0x0a, 0xd4, 0x1b, 0x16, 0x2d, 0xbc, 0x39,
|
||||
0xd1, 0x45, 0x70, 0x4a, 0xab, 0x46, 0x25, 0x3b, 0x47, 0x35, 0x8a, 0x30,
|
||||
0x54, 0x29, 0xd5, 0x1e, 0x34, 0x13, 0xa7, 0x0a, 0x02, 0x05, 0x83, 0xfc,
|
||||
0xde, 0xef, 0xd8, 0xe1, 0x61, 0xd6, 0xfb, 0xcb, 0x55, 0xc5, 0x52, 0xc3,
|
||||
0x99, 0xc5, 0x10, 0xcd, 0x65, 0xd7, 0xc6, 0xe3, 0xd5, 0xed, 0xb5, 0xf5,
|
||||
0x52, 0x00, 0x1e, 0x09, 0xe6, 0x0c, 0xae, 0x0d, 0x76, 0x0c, 0xf9, 0x0a,
|
||||
0x2e, 0x11, 0x9a, 0x17, 0x17, 0x1b, 0x71, 0x20, 0xd9, 0x22, 0x8d, 0x20,
|
||||
0xbd, 0x1a, 0x6e, 0x13, 0x8c, 0x0b, 0xe8, 0x08, 0x39, 0x07, 0x39, 0x06,
|
||||
0xbc, 0x08, 0x9b, 0x0b, 0x19, 0x10, 0x56, 0x12, 0x20, 0x12, 0x9e, 0x10,
|
||||
0xa9, 0x0b, 0x78, 0x05, 0x00, 0xff, 0xf6, 0xf6, 0xc9, 0xf0, 0x15, 0xe9,
|
||||
0xad, 0xe2, 0xa0, 0xdb, 0xcc, 0xd2, 0x03, 0xcc, 0x9d, 0xc4, 0x8a, 0xc2,
|
||||
0x60, 0xbd, 0x26, 0xbb, 0x26, 0xc2, 0xcd, 0xd5, 0x4b, 0xf0, 0x59, 0x0b,
|
||||
0xc8, 0x20, 0xe9, 0x32, 0xdc, 0x41, 0x5b, 0x47, 0x3f, 0x47, 0xc1, 0x42,
|
||||
0x30, 0x3e, 0x0d, 0x38, 0x81, 0x2d, 0xce, 0x22, 0x8e, 0x18, 0xd9, 0x11,
|
||||
0x94, 0x0a, 0x92, 0x03, 0xb6, 0xf6, 0x94, 0xe9, 0xe3, 0xdb, 0x78, 0xd2,
|
||||
0xf9, 0xcd, 0x19, 0xc9, 0x4e, 0xc7, 0x70, 0xcb, 0x3f, 0xd3, 0x24, 0xde,
|
||||
0x3b, 0xe9, 0x83, 0xf2, 0xfc, 0xfd, 0xa0, 0x0b, 0x60, 0x12, 0x53, 0x13,
|
||||
0x4c, 0x10, 0xd2, 0x10, 0x1f, 0x17, 0xfb, 0x19, 0x53, 0x1b, 0x51, 0x1a,
|
||||
0x21, 0x1b, 0x28, 0x19, 0x56, 0x15, 0xaf, 0x0e, 0x52, 0x05, 0x40, 0x01,
|
||||
0xe6, 0xff, 0x28, 0xff, 0x7c, 0x01, 0x81, 0x05, 0x90, 0x09, 0x1d, 0x12,
|
||||
0x40, 0x16, 0x51, 0x16, 0x81, 0x14, 0x24, 0x0f, 0x8b, 0x09, 0x5f, 0x02,
|
||||
0xa5, 0xf8, 0xc1, 0xef, 0x97, 0xe7, 0x83, 0xda, 0x4f, 0xd0, 0xb1, 0xc5,
|
||||
0xfa, 0xbd, 0x44, 0xb9, 0xdd, 0xb5, 0x5f, 0xb6, 0x9b, 0xc3, 0x7e, 0xda,
|
||||
0xda, 0xf7, 0x63, 0x13, 0x05, 0x26, 0xc1, 0x34, 0xd3, 0x3f, 0x02, 0x45,
|
||||
0x88, 0x46, 0x44, 0x42, 0x06, 0x3d, 0x67, 0x36, 0x4d, 0x2c, 0x93, 0x21,
|
||||
0x00, 0x16, 0x38, 0x0e, 0x03, 0x0b, 0x6b, 0x01, 0x11, 0xf1, 0x50, 0xe1,
|
||||
0x23, 0xd6, 0xf1, 0xcd, 0x1b, 0xcc, 0xe3, 0xc9, 0xcb, 0xc8, 0x3c, 0xd0,
|
||||
0xf6, 0xd9, 0x44, 0xe7, 0x2a, 0xf4, 0x65, 0xfe, 0x4f, 0x09, 0x45, 0x12,
|
||||
0x33, 0x16, 0xa5, 0x15, 0x57, 0x15, 0x83, 0x14, 0xd5, 0x16, 0xd1, 0x18,
|
||||
0x0a, 0x14, 0xf1, 0x12, 0xdf, 0x0f, 0x3d, 0x0c, 0xca, 0x09, 0x0c, 0x05,
|
||||
0x70, 0xfe, 0x26, 0xfa, 0xfa, 0xf9, 0x92, 0xfb, 0x88, 0x01, 0xfd, 0x08,
|
||||
0x67, 0x12, 0x56, 0x18, 0x44, 0x1a, 0xfa, 0x19, 0xaa, 0x16, 0x15, 0x11,
|
||||
0x00, 0x0c, 0x89, 0x05, 0x26, 0xfd, 0x2e, 0xf3, 0x01, 0xe5, 0xd2, 0xd6,
|
||||
0xe1, 0xcb, 0x76, 0xc1, 0x7c, 0xb6, 0xb2, 0xae, 0xb6, 0xaf, 0xc0, 0xbc,
|
||||
0x10, 0xd4, 0x4a, 0xf0, 0x21, 0x08, 0xdb, 0x19, 0xb4, 0x27, 0xbe, 0x34,
|
||||
0x17, 0x40, 0x22, 0x42, 0x17, 0x42, 0x34, 0x3d, 0x03, 0x36, 0x27, 0x2e,
|
||||
0x39, 0x24, 0xba, 0x1b, 0x19, 0x18, 0x0b, 0x12, 0x1e, 0x04, 0x34, 0xf2,
|
||||
0xf5, 0xe2, 0x19, 0xda, 0xe7, 0xd4, 0x30, 0xd1, 0x98, 0xce, 0x56, 0xd1,
|
||||
0x76, 0xd6, 0xf3, 0xde, 0x42, 0xe9, 0x55, 0xf3, 0x50, 0xfe, 0x35, 0x09,
|
||||
0x69, 0x0f, 0x05, 0x13, 0xd5, 0x14, 0xab, 0x17, 0x03, 0x1a, 0x75, 0x1a,
|
||||
0xf3, 0x16, 0x53, 0x11, 0x6f, 0x0c, 0xce, 0x09, 0xcf, 0x05, 0x5e, 0xff,
|
||||
0x47, 0xf9, 0x8b, 0xf4, 0xe7, 0xf3, 0x62, 0xf5, 0x39, 0xf9, 0x2c, 0x00,
|
||||
0x37, 0x0d, 0x52, 0x16, 0x6f, 0x1a, 0x6f, 0x1d, 0x64, 0x1c, 0x1d, 0x19,
|
||||
0xe4, 0x16, 0xbc, 0x11, 0x6f, 0x09, 0x2b, 0x01, 0x6e, 0xf3, 0x09, 0xe5,
|
||||
0xda, 0xd5, 0x23, 0xc7, 0xc9, 0xbd, 0x99, 0xb5, 0x14, 0xaf, 0x4a, 0xb2,
|
||||
0x4f, 0xbf, 0x82, 0xd7, 0x5c, 0xf4, 0x46, 0x0d, 0x93, 0x1c, 0xc0, 0x2b,
|
||||
0xe5, 0x37, 0xc8, 0x38, 0x85, 0x39, 0x39, 0x3b, 0xe5, 0x38, 0x1a, 0x35,
|
||||
0x41, 0x2f, 0xed, 0x24, 0xab, 0x1c, 0x59, 0x16, 0xe6, 0x0a, 0xee, 0xfb,
|
||||
0x97, 0xed, 0x9e, 0xe0, 0x8a, 0xd8, 0x03, 0xd4, 0x92, 0xd2, 0xfc, 0xd4,
|
||||
0xc2, 0xd7, 0x04, 0xe0, 0x5e, 0xe8, 0x11, 0xf0, 0x19, 0xf9, 0xb3, 0x01,
|
||||
0x3f, 0x0b, 0xd6, 0x10, 0x9a, 0x12, 0x4b, 0x15, 0x42, 0x15, 0x69, 0x16,
|
||||
0x44, 0x13, 0xd9, 0x0d, 0x26, 0x0b, 0x1f, 0x06, 0x13, 0xff, 0x09, 0xfb,
|
||||
0x6b, 0xf8, 0x71, 0xf6, 0x95, 0xf5, 0x88, 0xf6, 0x12, 0xf9, 0xbf, 0xff,
|
||||
0x9f, 0x08, 0x6d, 0x12, 0x4a, 0x1b, 0xef, 0x20, 0xac, 0x22, 0x06, 0x21,
|
||||
0x4a, 0x1b, 0x82, 0x14, 0x17, 0x0d, 0xe7, 0x02, 0x8c, 0xf6, 0x7d, 0xe6,
|
||||
0x7b, 0xd6, 0x7f, 0xc8, 0x04, 0xbb, 0x32, 0xb1, 0xe1, 0xac, 0xe0, 0xaf,
|
||||
0x3f, 0xc0, 0x79, 0xda, 0x08, 0xf4, 0x07, 0x0b, 0xa2, 0x1a, 0x31, 0x28,
|
||||
0x62, 0x34, 0x48, 0x39, 0xde, 0x3b, 0x58, 0x3a, 0xc1, 0x37, 0x22, 0x31,
|
||||
0xcf, 0x2d, 0xa5, 0x23, 0x6b, 0x1c, 0xb5, 0x11, 0xe6, 0x05, 0x71, 0xfb,
|
||||
0x92, 0xe9, 0x67, 0xdc, 0xe2, 0xd1, 0x0e, 0xd0, 0xf5, 0xd1, 0xe8, 0xd6,
|
||||
0x80, 0xde, 0xb4, 0xe6, 0x54, 0xf0, 0x6d, 0xfa, 0x1a, 0x02, 0x56, 0x06,
|
||||
0x52, 0x0b, 0x12, 0x10, 0xfc, 0x12, 0x2a, 0x14, 0xe5, 0x12, 0x3a, 0x11,
|
||||
0x96, 0x10, 0xad, 0x0d, 0x71, 0x07, 0xe0, 0x00, 0x9c, 0xfc, 0x6a, 0xf7,
|
||||
0x77, 0xf5, 0x45, 0xf5, 0xf4, 0xf5, 0x87, 0xf8, 0x85, 0xfd, 0x71, 0x04,
|
||||
0x64, 0x0b, 0x63, 0x16, 0xff, 0x1c, 0x5f, 0x1f, 0x6f, 0x20, 0x71, 0x1d,
|
||||
0x73, 0x1b, 0x45, 0x12, 0xd4, 0x09, 0x6a, 0xfe, 0xcb, 0xed, 0x8f, 0xdc,
|
||||
0xd0, 0xcc, 0xa4, 0xbd, 0xe6, 0xb5, 0x32, 0xb0, 0x4c, 0xb3, 0xea, 0xbb,
|
||||
0xe1, 0xd0, 0xb2, 0xee, 0x3e, 0x09, 0x3e, 0x1c, 0xf4, 0x25, 0x54, 0x2f,
|
||||
0x0b, 0x33, 0xb0, 0x39, 0x16, 0x3a, 0x72, 0x36, 0x12, 0x33, 0xbb, 0x2c,
|
||||
0x21, 0x24, 0x3d, 0x1a, 0xd9, 0x10, 0x09, 0x07, 0x2f, 0xfc, 0x88, 0xef,
|
||||
0x90, 0xe0, 0x3c, 0xd4, 0x4b, 0xcd, 0xd3, 0xd1, 0x2f, 0xdb, 0xb1, 0xe2,
|
||||
0xc1, 0xea, 0x8a, 0xf3, 0x5d, 0xfd, 0x68, 0x06, 0x23, 0x09, 0xa4, 0x0b,
|
||||
0x7d, 0x10, 0x50, 0x15, 0x8e, 0x15, 0xfc, 0x11, 0xea, 0x0d, 0x74, 0x0d,
|
||||
0x88, 0x0b, 0x07, 0x08, 0x7d, 0x00, 0xf6, 0xf9, 0xef, 0xf2, 0x2d, 0xf2,
|
||||
0xce, 0xf4, 0x94, 0xf4, 0xe1, 0xf7, 0x5e, 0xfd, 0x20, 0x04, 0xe8, 0x0d,
|
||||
0x1e, 0x15, 0xc4, 0x1a, 0xd6, 0x1f, 0x7a, 0x20, 0xfc, 0x1c, 0x72, 0x1d,
|
||||
0x78, 0x16, 0x80, 0x0d, 0x24, 0x03, 0x87, 0xef, 0xc6, 0xde, 0x64, 0xcc,
|
||||
0x62, 0xbf, 0x2a, 0xb6, 0x7c, 0xaf, 0xc9, 0xb0, 0xbe, 0xbb, 0xe0, 0xd1,
|
||||
0x9d, 0xed, 0x1c, 0x06, 0x3f, 0x18, 0x6b, 0x22, 0x32, 0x2d, 0xf7, 0x32,
|
||||
0x51, 0x36, 0xf4, 0x3b, 0x87, 0x39, 0xcf, 0x33, 0xa6, 0x2b, 0x20, 0x22,
|
||||
0x7d, 0x1c, 0xbe, 0x13, 0xba, 0x09, 0x73, 0xfb, 0x96, 0xec, 0x06, 0xdd,
|
||||
0x22, 0xd6, 0x9a, 0xd3, 0x8d, 0xda, 0x0b, 0xe1, 0xc8, 0xe6, 0x78, 0xeb,
|
||||
0xa6, 0xf1, 0x68, 0xf7, 0x34, 0x02, 0x52, 0x0a, 0x21, 0x0e, 0x65, 0x13,
|
||||
0xcf, 0x13, 0xea, 0x12, 0xce, 0x11, 0x27, 0x0e, 0xd1, 0x0b, 0x82, 0x0a,
|
||||
0x38, 0x05, 0x0e, 0xff, 0x1e, 0xf7, 0xc4, 0xf2, 0x23, 0xf2, 0x7c, 0xf4,
|
||||
0x43, 0xf7, 0xc7, 0xfa, 0x5e, 0xff, 0x89, 0x04, 0xab, 0x0d, 0xef, 0x14,
|
||||
0x26, 0x1d, 0x4d, 0x21, 0x0b, 0x21, 0xc6, 0x1c, 0x54, 0x16, 0x12, 0x0f,
|
||||
0xd6, 0x06, 0xab, 0xf9, 0xe2, 0xe7, 0x37, 0xd8, 0x46, 0xc9, 0xc4, 0xbe,
|
||||
0x3c, 0xb6, 0x8e, 0xb2, 0xb9, 0xb6, 0x41, 0xc8, 0x13, 0xe0, 0xd4, 0xf8,
|
||||
0x7b, 0x0e, 0xe1, 0x1c, 0x7d, 0x23, 0x07, 0x2c, 0x3a, 0x2f, 0x15, 0x32,
|
||||
0xd3, 0x30, 0xc1, 0x2f, 0x44, 0x2c, 0x64, 0x27, 0xc7, 0x20, 0x04, 0x18,
|
||||
0xb3, 0x10, 0x20, 0x05, 0x09, 0xfa, 0x86, 0xee, 0x9f, 0xe1, 0xee, 0xda,
|
||||
0x6f, 0xd9, 0x7c, 0xdd, 0x19, 0xe6, 0xb5, 0xf0, 0x1e, 0xf6, 0x21, 0xfb,
|
||||
0xba, 0xfe, 0x68, 0x01, 0x45, 0x07, 0x5e, 0x09, 0xbb, 0x0c, 0x61, 0x0c,
|
||||
0xda, 0x0c, 0xac, 0x0e, 0x3e, 0x0d, 0xb1, 0x09, 0x03, 0x05, 0xe9, 0xfe,
|
||||
0xcb, 0xf9, 0x21, 0xf6, 0xc3, 0xf2, 0x6f, 0xf2, 0xbf, 0xf5, 0x56, 0xfa,
|
||||
0x85, 0x00, 0x88, 0x07, 0x3a, 0x0d, 0x78, 0x15, 0xfd, 0x1b, 0x5c, 0x20,
|
||||
0x30, 0x21, 0x79, 0x1d, 0xce, 0x19, 0x84, 0x13, 0x1e, 0x0a, 0x0c, 0xfe,
|
||||
0x89, 0xed, 0x63, 0xd9, 0x5f, 0xca, 0x4a, 0xbd, 0xc9, 0xb5, 0x59, 0xb3,
|
||||
0xbb, 0xb8, 0x68, 0xc4, 0xfa, 0xdb, 0x7c, 0xf3, 0x11, 0x05, 0xc6, 0x17,
|
||||
0x18, 0x21, 0x47, 0x2f, 0x1a, 0x31, 0x7f, 0x30, 0xd8, 0x31, 0xb6, 0x30,
|
||||
0xd2, 0x2d, 0xe0, 0x27, 0x12, 0x21, 0x58, 0x19, 0x25, 0x11, 0x06, 0x08,
|
||||
0x84, 0xf7, 0xad, 0xe9, 0xe5, 0xdb, 0xb0, 0xd8, 0x15, 0xdc, 0x07, 0xe0,
|
||||
0x10, 0xe8, 0x9e, 0xef, 0xc6, 0xf7, 0xef, 0xff, 0xab, 0x05, 0xd8, 0x0a,
|
||||
0xbf, 0x0e, 0x0d, 0x11, 0x32, 0x10, 0xb1, 0x10, 0xc0, 0x0f, 0x81, 0x0d,
|
||||
0xe2, 0x0c, 0xaf, 0x07, 0x2a, 0x04, 0x2d, 0xfe, 0x29, 0xf7, 0x9b, 0xf2,
|
||||
0x1d, 0xf0, 0x00, 0xef, 0xbe, 0xf2, 0x4a, 0xf5, 0x6a, 0xf9, 0x9d, 0x01,
|
||||
0x6e, 0x0a, 0x44, 0x11, 0x35, 0x1a, 0x52, 0x1e, 0xa5, 0x1e, 0xf4, 0x1c,
|
||||
0x36, 0x18, 0xd3, 0x10, 0xa7, 0x09, 0x3b, 0xfa, 0x47, 0xeb, 0xa8, 0xdd,
|
||||
0x85, 0xcd, 0x45, 0xc4, 0x04, 0xbc, 0x7b, 0xb8, 0x3b, 0xbf, 0x26, 0xcb,
|
||||
0x2e, 0xe1, 0x54, 0xf7, 0xf8, 0x07, 0x0b, 0x18, 0x1f, 0x1f, 0xd0, 0x24,
|
||||
0xbf, 0x2a, 0x2e, 0x2d, 0x89, 0x2e, 0x55, 0x2d, 0xa2, 0x26, 0x49, 0x21,
|
||||
0x63, 0x1e, 0x3e, 0x17, 0x0b, 0x11, 0xf6, 0x05, 0x71, 0xf8, 0x95, 0xed,
|
||||
0x3c, 0xe6, 0x97, 0xe2, 0xbe, 0xe3, 0xf3, 0xe5, 0xae, 0xed, 0x09, 0xf7,
|
||||
0x73, 0xfc, 0x14, 0x02, 0xbf, 0x09, 0x06, 0x0b, 0xab, 0x0d, 0x4d, 0x0f,
|
||||
0x59, 0x0e, 0x99, 0x0d, 0x11, 0x0c, 0xa6, 0x08, 0x0d, 0x06, 0x7d, 0x02,
|
||||
0xeb, 0xfd, 0xd5, 0xfa, 0x6b, 0xf7, 0x0a, 0xf4, 0x6e, 0xf1, 0x7d, 0xef,
|
||||
0xfa, 0xf0, 0x1a, 0xf7, 0xf7, 0xfa, 0x20, 0x01, 0x60, 0x09, 0x59, 0x12,
|
||||
0xff, 0x16, 0x46, 0x1d, 0x81, 0x1c, 0x27, 0x1a, 0x26, 0x12, 0x42, 0x07,
|
||||
0xbd, 0xf9, 0x07, 0xf1, 0xb7, 0xe6, 0x9c, 0xd9, 0x29, 0xce, 0xc8, 0xc7,
|
||||
0x8f, 0xc1, 0x04, 0xc4, 0xb4, 0xd0, 0xc0, 0xe0, 0x9c, 0xf3, 0xb1, 0xff,
|
||||
0x64, 0x0b, 0x5c, 0x15, 0xf5, 0x1b, 0x82, 0x20, 0x1b, 0x2a, 0x9e, 0x28,
|
||||
0xd5, 0x2a, 0xb9, 0x28, 0xb9, 0x21, 0xc1, 0x1a, 0x27, 0x14, 0x4d, 0x0d,
|
||||
0x1f, 0x07, 0x9f, 0x01, 0x7f, 0xf6, 0x8b, 0xee, 0xaf, 0xe8, 0x15, 0xe7,
|
||||
0xd2, 0xec, 0x70, 0xf1, 0xff, 0xf5, 0xf1, 0xfb, 0x4a, 0xff, 0x0e, 0x02,
|
||||
0xf5, 0x05, 0xfc, 0x05, 0xf2, 0x0a, 0xf4, 0x0d, 0x57, 0x0e, 0x81, 0x0c,
|
||||
0xfb, 0x0a, 0xe8, 0x06, 0x6d, 0x03, 0x6e, 0xff, 0x8f, 0xfb, 0x77, 0xf8,
|
||||
0x8f, 0xf3, 0x48, 0xee, 0x89, 0xee, 0x36, 0xf0, 0x17, 0xf5, 0xe0, 0xfc,
|
||||
0x53, 0x02, 0xde, 0x07, 0x75, 0x0e, 0xce, 0x12, 0xaf, 0x16, 0xc0, 0x16,
|
||||
0x8a, 0x14, 0xd6, 0x12, 0x2a, 0x0a, 0xe7, 0x00, 0x03, 0xf7, 0x6c, 0xe9,
|
||||
0x69, 0xdc, 0x08, 0xd1, 0xd8, 0xc5, 0xf8, 0xc3, 0x6d, 0xc8, 0x71, 0xd6,
|
||||
0xb5, 0xe7, 0x0b, 0xf3, 0xd1, 0xfd, 0x55, 0x07, 0xd3, 0x0d, 0x57, 0x18,
|
||||
0x10, 0x19, 0xea, 0x1d, 0x23, 0x23, 0x62, 0x25, 0x34, 0x28, 0xb5, 0x23,
|
||||
0x48, 0x20, 0x6d, 0x1b, 0xc5, 0x13, 0xd1, 0x0a, 0xc9, 0x02, 0xfb, 0xfa,
|
||||
0x67, 0xf2, 0xc2, 0xed, 0x64, 0xea, 0x29, 0xed, 0x8a, 0xf0, 0xe8, 0xf3,
|
||||
0x95, 0xf8, 0x87, 0xfd, 0x1b, 0x02, 0x1f, 0x07, 0xd0, 0x0a, 0xb3, 0x0d,
|
||||
0x2c, 0x0f, 0x34, 0x0e, 0x7c, 0x0b, 0xc1, 0x0b, 0xfe, 0x09, 0x7a, 0x08,
|
||||
0xd9, 0x04, 0x31, 0xff, 0x4d, 0xf9, 0x23, 0xf3, 0x06, 0xef, 0x26, 0xec,
|
||||
0x46, 0xee, 0x18, 0xf0, 0xea, 0xf3, 0x0f, 0xfb, 0xa7, 0x01, 0x96, 0x07,
|
||||
0xcb, 0x0b, 0x39, 0x0f, 0x19, 0x12, 0x1e, 0x12, 0x1c, 0x0d, 0xfc, 0x07,
|
||||
0xb0, 0xff, 0x84, 0xf6, 0x35, 0xea, 0x44, 0xdc, 0x9f, 0xcf, 0xbf, 0xc8,
|
||||
0xcc, 0xcb, 0x41, 0xd5, 0x4c, 0xe0, 0x77, 0xee, 0xb4, 0xfa, 0x2e, 0x07,
|
||||
0xc7, 0x0f, 0x82, 0x15, 0x64, 0x16, 0xb9, 0x19, 0xaa, 0x1c, 0x51, 0x1b,
|
||||
0x74, 0x1f, 0x76, 0x1d, 0x52, 0x1c, 0x47, 0x1a, 0x1b, 0x14, 0xec, 0x0e,
|
||||
0xe9, 0x05, 0xb6, 0xfc, 0x48, 0xf7, 0x42, 0xf2, 0xcb, 0xee, 0x38, 0xee,
|
||||
0x03, 0xef, 0xcb, 0xee, 0x42, 0xf5, 0x83, 0xf9, 0xed, 0xff, 0x16, 0x08,
|
||||
0x18, 0x0d, 0x4b, 0x0f, 0x87, 0x13, 0x4f, 0x12, 0x72, 0x10, 0xf6, 0x12,
|
||||
0x3e, 0x10, 0x1a, 0x0f, 0xcb, 0x0a, 0x85, 0x03, 0x3d, 0xfd, 0xc9, 0xf8,
|
||||
0x81, 0xf3, 0xab, 0xf1, 0xe5, 0xef, 0xe3, 0xee, 0x96, 0xf1, 0xb7, 0xf4,
|
||||
0x6d, 0xf9, 0xd6, 0x01, 0x6f, 0x06, 0xdd, 0x0a, 0xc1, 0x0c, 0xd7, 0x0b,
|
||||
0xd8, 0x08, 0xdf, 0x03, 0x27, 0xfe, 0x5b, 0xf7, 0x7e, 0xef, 0x49, 0xe6,
|
||||
0x6a, 0xdf, 0xe3, 0xd8, 0xd8, 0xd5, 0xa3, 0xda, 0x57, 0xe3, 0x59, 0xea,
|
||||
0x4a, 0xf3, 0xc0, 0xfa, 0x49, 0x01, 0x40, 0x09, 0x31, 0x11, 0x20, 0x16,
|
||||
0xce, 0x17, 0x79, 0x19, 0x14, 0x1c, 0x5c, 0x1c, 0x1a, 0x1b, 0xfe, 0x15,
|
||||
0xcb, 0x13, 0x74, 0x12, 0x5b, 0x0e, 0xeb, 0x08, 0x79, 0x01, 0x6e, 0xfb,
|
||||
0x1f, 0xf7, 0x31, 0xf4, 0x0d, 0xf3, 0x58, 0xf3, 0xb5, 0xf6, 0xff, 0xf5,
|
||||
0x25, 0xf9, 0x82, 0xfb, 0x38, 0xff, 0xb0, 0x03, 0x83, 0x08, 0xfb, 0x0c,
|
||||
0x94, 0x11, 0x40, 0x14, 0x00, 0x14, 0x3b, 0x11, 0x3e, 0x0c, 0x17, 0x0a,
|
||||
0xfd, 0x05, 0x31, 0x01, 0x97, 0xfb, 0xdb, 0xf5, 0xc7, 0xf1, 0x0e, 0xf1,
|
||||
0x94, 0xf1, 0x2b, 0xf5, 0x99, 0xf9, 0x68, 0xfa, 0xf0, 0xfb, 0x3b, 0xfe,
|
||||
0xb7, 0x00, 0xa0, 0x03, 0x64, 0x06, 0x92, 0x05, 0xe5, 0x03, 0xbf, 0xfd,
|
||||
0x81, 0xf9, 0xde, 0xf0, 0xb2, 0xe7, 0x36, 0xdf, 0xa8, 0xd6, 0x0a, 0xd3,
|
||||
0x1d, 0xd9, 0x4d, 0xe2, 0x6d, 0xed, 0x82, 0xf5, 0xf0, 0xfb, 0x30, 0x03,
|
||||
0x16, 0x0a, 0x12, 0x0f, 0x2f, 0x13, 0x54, 0x17, 0x51, 0x17, 0xfe, 0x1d,
|
||||
0x22, 0x22, 0x7f, 0x21, 0xcb, 0x21, 0x4b, 0x1d, 0x79, 0x15, 0xd3, 0x0d,
|
||||
0xa6, 0x06, 0x12, 0xff, 0xc6, 0xfb, 0xef, 0xf6, 0x4d, 0xf3, 0x1d, 0xf2,
|
||||
0xf8, 0xef, 0x5d, 0xf2, 0xcc, 0xf6, 0xff, 0xf9, 0xd7, 0xfc, 0x82, 0x00,
|
||||
0xb2, 0x02, 0x04, 0x07, 0x0b, 0x0d, 0x55, 0x0f, 0x43, 0x11, 0x83, 0x12,
|
||||
0x19, 0x0d, 0x77, 0x0a, 0xf3, 0x05, 0xd1, 0x02, 0x48, 0xff, 0x59, 0xf9,
|
||||
0xd4, 0xf3, 0x73, 0xf0, 0x20, 0xf2, 0x46, 0xf4, 0x2b, 0xf8, 0x90, 0xfb,
|
||||
0xa4, 0xfc, 0x2b, 0xfe, 0x16, 0x03, 0xb0, 0x07, 0x2d, 0x0b, 0xee, 0x08,
|
||||
0xdd, 0x08, 0xc9, 0x06, 0x91, 0xff, 0xc1, 0xfa, 0x1e, 0xf1, 0x3d, 0xeb,
|
||||
0x39, 0xe4, 0x0d, 0xdf, 0x75, 0xd8, 0x1d, 0xd7, 0x25, 0xd7, 0x03, 0xde,
|
||||
0xb4, 0xe8, 0x24, 0xf1, 0x10, 0xfb, 0x9c, 0x06, 0x52, 0x0b, 0x8e, 0x12,
|
||||
0xa8, 0x16, 0xc5, 0x1b, 0x89, 0x20, 0xb8, 0x21, 0x03, 0x21, 0x51, 0x1d,
|
||||
0xc8, 0x17, 0x63, 0x14, 0x7f, 0x12, 0xa8, 0x0d, 0xa9, 0x09, 0x9e, 0x02,
|
||||
0x9b, 0xfa, 0xfb, 0xf6, 0x1b, 0xf2, 0x6d, 0xef, 0x15, 0xf1, 0x80, 0xf3,
|
||||
0x3f, 0xf6, 0xb6, 0xfc, 0x25, 0x00, 0x86, 0x05, 0x78, 0x0a, 0x4b, 0x0e,
|
||||
0x2d, 0x11, 0x02, 0x11, 0x49, 0x0f, 0x8d, 0x0d, 0x37, 0x08, 0x81, 0x03,
|
||||
0x15, 0xff, 0x92, 0xf8, 0xa2, 0xf5, 0x0a, 0xf3, 0x6f, 0xef, 0x19, 0xee,
|
||||
0xa9, 0xf1, 0xf2, 0xf3, 0x44, 0xfc, 0x01, 0x02, 0x32, 0x05, 0x34, 0x08,
|
||||
0xaf, 0x08, 0xc2, 0x07, 0x8a, 0x0b, 0xdc, 0x0a, 0x36, 0x08, 0xc4, 0x04,
|
||||
0xb8, 0xff, 0xc4, 0xfa, 0x8b, 0xf5, 0xbb, 0xef, 0x25, 0xe8, 0xdc, 0xde,
|
||||
0x56, 0xd9, 0x4f, 0xd4, 0xc4, 0xd8, 0xee, 0xdf, 0xc4, 0xe9, 0x1b, 0xf5,
|
||||
0xb5, 0xfe, 0xc5, 0x05, 0x33, 0x09, 0xbb, 0x0a, 0x88, 0x0d, 0xb7, 0x0d,
|
||||
0xcc, 0x12, 0xf3, 0x1a, 0xbc, 0x1a, 0xf2, 0x1a, 0x12, 0x1b, 0x96, 0x17,
|
||||
0x2c, 0x16, 0x94, 0x11, 0xb1, 0x0a, 0xf2, 0x04, 0x84, 0x01, 0xff, 0xff,
|
||||
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):
|
||||
for x in range(0, width):
|
||||
graphics.set_pen(graphics.create_pen(int((r * x) / 52), int((g * x) / 52), int((b * x) / 52)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def grid(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
if (x + y) % 2 == 0:
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def outline_text(text):
|
||||
ms = time.ticks_ms()
|
||||
|
||||
graphics.set_font("bitmap8")
|
||||
v = int((math.sin(ms / 100.0) + 1.0) * 127.0)
|
||||
w = graphics.measure_text(text, 1)
|
||||
|
||||
x = int(53 / 2 - w / 2 + 1)
|
||||
y = 2
|
||||
|
||||
graphics.set_pen(0)
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(v, v, v))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
was_a_pressed = False
|
||||
was_b_pressed = False
|
||||
was_c_pressed = False
|
||||
was_d_pressed = False
|
||||
was_z_pressed = False
|
||||
|
||||
bool_playing = False
|
||||
freq_a = 0
|
||||
freq_b = 0
|
||||
|
||||
beat = 0
|
||||
|
||||
channel0 = gu.synth_channel(0)
|
||||
channel1 = gu.synth_channel(1)
|
||||
channel2 = gu.synth_channel(2)
|
||||
channel3 = gu.synth_channel(3)
|
||||
channel4 = gu.synth_channel(7)
|
||||
channels = [channel0, channel1, channel2, channel3, channel4]
|
||||
|
||||
|
||||
def next_beat():
|
||||
global beat
|
||||
for i in range(5):
|
||||
if notes[i][beat] > 0:
|
||||
channels[i].frequency(notes[i][beat])
|
||||
channels[i].trigger_attack()
|
||||
elif notes[i][beat] == -1:
|
||||
channels[i].trigger_release()
|
||||
|
||||
beat = (beat + 1) % SONG_LENGTH
|
||||
|
||||
|
||||
def tick(timer):
|
||||
next_beat()
|
||||
|
||||
|
||||
timer = Timer(-1)
|
||||
|
||||
synthing = False
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
test = (time_ms // 1000) % 5
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
if not was_a_pressed:
|
||||
channel0.configure(waveforms=Channel.TRIANGLE + Channel.SQUARE,
|
||||
attack=0.016,
|
||||
decay=0.168,
|
||||
sustain=0,
|
||||
release=0.168,
|
||||
volume=0)
|
||||
channel1.configure(waveforms=Channel.SINE + Channel.SQUARE,
|
||||
attack=0.038,
|
||||
decay=0.300,
|
||||
sustain=0,
|
||||
release=0,
|
||||
volume=12000 / 65535)
|
||||
channel2.configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.010,
|
||||
sustain=16000 / 65535,
|
||||
release=0.100,
|
||||
volume=0)
|
||||
channel3.configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.005,
|
||||
sustain=8000 / 65535,
|
||||
release=0.040,
|
||||
volume=0)
|
||||
channel4.configure(waveforms=Channel.SQUARE,
|
||||
attack=0.010,
|
||||
decay=0.100,
|
||||
sustain=0,
|
||||
release=0.500,
|
||||
volume=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
|
||||
channel0.play_tone(freq_a, 0.06)
|
||||
|
||||
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
|
||||
|
||||
channel1.play_tone(freq_b, 0.06, attack=0.5)
|
||||
|
||||
gu.play_synth()
|
||||
synthing = False
|
||||
|
||||
bool_playing = True
|
||||
|
||||
was_c_pressed = True
|
||||
else:
|
||||
was_c_pressed = False
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_D):
|
||||
if not was_d_pressed:
|
||||
freq_a = 0
|
||||
freq_b = 0
|
||||
gu.stop_playing()
|
||||
timer.deinit()
|
||||
synthing = False
|
||||
was_d_pressed = True
|
||||
else:
|
||||
was_d_pressed = False
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
# gu.adjust_brightness(+0.01)
|
||||
if bool_playing:
|
||||
freq_b = min(freq_b + 10, 20000)
|
||||
channel1.frequency(freq_b)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
# gu.adjust_brightness(-0.01)
|
||||
if bool_playing:
|
||||
freq_b = max(freq_b - 10, 10)
|
||||
channel1.frequency(max(freq_b, 10))
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
|
||||
if bool_playing:
|
||||
freq_a = min(freq_a + 10, 20000)
|
||||
channel0.frequency(freq_a)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
|
||||
if bool_playing:
|
||||
freq_a = max(freq_a - 10, 10)
|
||||
channel0.frequency(freq_a)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||
if not was_z_pressed:
|
||||
channel0.configure(waveforms=Channel.TRIANGLE + Channel.SQUARE,
|
||||
attack=0.016,
|
||||
decay=0.168,
|
||||
sustain=0xafff / 65535,
|
||||
release=0.168,
|
||||
volume=10000 / 65535)
|
||||
channel1.configure(waveforms=Channel.SINE + Channel.SQUARE,
|
||||
attack=0.038,
|
||||
decay=0.300,
|
||||
sustain=0,
|
||||
release=0,
|
||||
volume=12000 / 65535)
|
||||
channel2.configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.010,
|
||||
sustain=16000 / 65535,
|
||||
release=0.100,
|
||||
volume=18000 / 65535)
|
||||
channel3.configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.005,
|
||||
sustain=8000 / 65535,
|
||||
release=0.040,
|
||||
volume=8000 / 65535)
|
||||
channel4.configure(waveforms=Channel.SQUARE,
|
||||
attack=0.010,
|
||||
decay=0.100,
|
||||
sustain=0,
|
||||
release=0.500,
|
||||
volume=12000 / 65535)
|
||||
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
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if test == 0:
|
||||
# print("grid pattern")
|
||||
grid(255, 255, 255)
|
||||
elif test == 1:
|
||||
# print("red gradient")
|
||||
gradient(255, 0, 0)
|
||||
elif test == 2:
|
||||
# print("green gradient")
|
||||
gradient(0, 255, 0)
|
||||
elif test == 3:
|
||||
# print("blue gradient")
|
||||
gradient(0, 0, 255)
|
||||
elif test == 4:
|
||||
# print("white gradient")
|
||||
gradient(255, 255, 255)
|
||||
|
||||
text = ""
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
text = "Play Sample"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_B):
|
||||
text = "Tone A"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_C):
|
||||
text = "Tone B"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_D):
|
||||
text = "Stop"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
|
||||
text = "Raise A"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
|
||||
text = "Lower A"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
text = "Raise B"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
text = "Power B"
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||
text = "Play Synth"
|
||||
|
||||
outline_text(text)
|
||||
|
||||
gu.update(graphics)
|
|
@ -0,0 +1,135 @@
|
|||
import time
|
||||
import random
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
fire_colours = [graphics.create_pen(0, 0, 0),
|
||||
graphics.create_pen(20, 20, 20),
|
||||
graphics.create_pen(180, 30, 0),
|
||||
graphics.create_pen(220, 160, 0),
|
||||
graphics.create_pen(255, 255, 180)]
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup_landscape():
|
||||
global width, height, heat, fire_spawns, damping_factor
|
||||
width = GalacticUnicorn.WIDTH + 2
|
||||
height = GalacticUnicorn.HEIGHT + 4
|
||||
heat = [[0.0 for y in range(height)] for x in range(width)]
|
||||
fire_spawns = 5
|
||||
damping_factor = 0.97
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup_portrait():
|
||||
global width, height, heat, fire_spawns, damping_factor
|
||||
width = GalacticUnicorn.HEIGHT + 2
|
||||
height = GalacticUnicorn.WIDTH + 4
|
||||
heat = [[0.0 for y in range(height)] for x in range(width)]
|
||||
fire_spawns = 2
|
||||
damping_factor = 0.99
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def update():
|
||||
# clear the bottom row and then add a new fire seed to it
|
||||
for x in range(width):
|
||||
heat[x][height - 1] = 0.0
|
||||
heat[x][height - 2] = 0.0
|
||||
|
||||
for c in range(fire_spawns):
|
||||
x = random.randint(0, width - 4) + 2
|
||||
heat[x + 0][height - 1] = 1.0
|
||||
heat[x + 1][height - 1] = 1.0
|
||||
heat[x - 1][height - 1] = 1.0
|
||||
heat[x + 0][height - 2] = 1.0
|
||||
heat[x + 1][height - 2] = 1.0
|
||||
heat[x - 1][height - 2] = 1.0
|
||||
|
||||
for y in range(0, height - 2):
|
||||
for x in range(1, width - 1):
|
||||
# update this pixel by averaging the below pixels
|
||||
average = (
|
||||
heat[x][y] + heat[x][y + 1] + heat[x][y + 2] + heat[x - 1][y + 1] + heat[x + 1][y + 1]
|
||||
) / 5.0
|
||||
|
||||
# damping factor to ensure flame tapers out towards the top of the displays
|
||||
average *= damping_factor
|
||||
|
||||
# update the heat map with our newly averaged value
|
||||
heat[x][y] = average
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw_landscape():
|
||||
for y in range(GalacticUnicorn.HEIGHT):
|
||||
for x in range(GalacticUnicorn.WIDTH):
|
||||
value = heat[x + 1][y]
|
||||
if value < 0.15:
|
||||
graphics.set_pen(fire_colours[0])
|
||||
elif value < 0.25:
|
||||
graphics.set_pen(fire_colours[1])
|
||||
elif value < 0.35:
|
||||
graphics.set_pen(fire_colours[2])
|
||||
elif value < 0.45:
|
||||
graphics.set_pen(fire_colours[3])
|
||||
else:
|
||||
graphics.set_pen(fire_colours[4])
|
||||
graphics.pixel(x, y)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw_portrait():
|
||||
for y in range(GalacticUnicorn.WIDTH):
|
||||
for x in range(GalacticUnicorn.HEIGHT):
|
||||
value = heat[x + 1][y]
|
||||
if value < 0.15:
|
||||
graphics.set_pen(fire_colours[0])
|
||||
elif value < 0.25:
|
||||
graphics.set_pen(fire_colours[1])
|
||||
elif value < 0.35:
|
||||
graphics.set_pen(fire_colours[2])
|
||||
elif value < 0.45:
|
||||
graphics.set_pen(fire_colours[3])
|
||||
else:
|
||||
graphics.set_pen(fire_colours[4])
|
||||
graphics.pixel(y, x)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
landscape = True
|
||||
setup_landscape()
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
landscape = True
|
||||
setup_landscape()
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_B):
|
||||
landscape = False
|
||||
setup_portrait()
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
update()
|
||||
if landscape:
|
||||
draw_landscape()
|
||||
else:
|
||||
draw_portrait()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,11 @@
|
|||
# Galactic Paint
|
||||
|
||||
Galactic Paint lets you paint pixels onto your Galatic Unicorn over WiFi, in realtime!
|
||||
|
||||
## Setting Up
|
||||
|
||||
You'll need `WIFI_CONFIG.py` from the `common` directory to be saved to your Pico W. Open up `WIFI_CONFIG.py` in Thonny to add your wifi details (and save it when you're done).
|
||||
|
||||
You will also have to install `micropython-phew` and `microdot` through Thonny's Tools -> Manage Packages.
|
||||
|
||||
Run the example through Thonny and it should get connected and give you a URL to visit. Open that URL in your browser and start painting!
|
|
@ -0,0 +1,113 @@
|
|||
import os
|
||||
from microdot_asyncio import Microdot, send_file
|
||||
from microdot_asyncio_websocket import with_websocket
|
||||
from phew import connect_to_wifi
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
from WIFI_CONFIG import SSID, PSK
|
||||
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
mv_graphics = memoryview(graphics)
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
WIDTH, HEIGHT = graphics.get_bounds()
|
||||
|
||||
ip = connect_to_wifi(SSID, PSK)
|
||||
|
||||
print(f"Start painting at: http://{ip}")
|
||||
|
||||
|
||||
server = Microdot()
|
||||
|
||||
|
||||
@server.route("/", methods=["GET"])
|
||||
def route_index(request):
|
||||
return send_file("galactic_paint/index.html")
|
||||
|
||||
|
||||
@server.route("/static/<path:path>", methods=["GET"])
|
||||
def route_static(request, path):
|
||||
return send_file(f"galactic_paint/static/{path}")
|
||||
|
||||
|
||||
def get_pixel(x, y):
|
||||
if x < WIDTH and y < HEIGHT and x >= 0 and y >= 0:
|
||||
o = (y * WIDTH + x) * 4
|
||||
return tuple(mv_graphics[o:o + 3])
|
||||
return None
|
||||
|
||||
|
||||
def flood_fill(x, y, r, g, b):
|
||||
todo = []
|
||||
|
||||
def fill(x, y, c):
|
||||
if get_pixel(x, y) != c:
|
||||
return
|
||||
|
||||
graphics.pixel(x, y)
|
||||
|
||||
up = get_pixel(x, y - 1)
|
||||
dn = get_pixel(x, y + 1)
|
||||
lf = get_pixel(x - 1, y)
|
||||
ri = get_pixel(x + 1, y)
|
||||
|
||||
if up == c:
|
||||
todo.append((x, y - 1))
|
||||
if dn == c:
|
||||
todo.append((x, y + 1))
|
||||
if lf == c:
|
||||
todo.append((x - 1, y))
|
||||
if ri == c:
|
||||
todo.append((x + 1, y))
|
||||
|
||||
c = get_pixel(x, y)
|
||||
|
||||
if c is None:
|
||||
return
|
||||
|
||||
fill(x, y, c)
|
||||
|
||||
while len(todo):
|
||||
x, y = todo.pop(0)
|
||||
fill(x, y, c)
|
||||
|
||||
|
||||
@server.route('/paint')
|
||||
@with_websocket
|
||||
async def echo(request, ws):
|
||||
while True:
|
||||
data = await ws.receive()
|
||||
try:
|
||||
x, y, r, g, b = [int(n) for n in data[0:5]]
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
except ValueError:
|
||||
if data == "show":
|
||||
gu.update(graphics)
|
||||
|
||||
if data == "fill":
|
||||
data = await ws.receive()
|
||||
x, y, r, g, b = [int(n) for n in data[0:5]]
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
flood_fill(x, y, r, g, b)
|
||||
|
||||
if data == "clear":
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if data == "save":
|
||||
filename = await ws.receive()
|
||||
print(f"Saving to {filename}.bin")
|
||||
try:
|
||||
os.mkdir("saves")
|
||||
except OSError:
|
||||
pass
|
||||
with open(f"saves/{filename}.bin", "wb") as f:
|
||||
f.write(graphics)
|
||||
await ws.send(f"alert: Saved to saves/{filename}.bin")
|
||||
|
||||
|
||||
server.run(host="0.0.0.0", port=80)
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Galactic Paint</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="//cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="/static/paint.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="window">
|
||||
<h1>Galactic Paint</h1>
|
||||
<table cellspacing="0" cellpadding="0" border-collapse="collapse">
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<div id="palette">
|
||||
<ul>
|
||||
<li class="selected" style="background:rgb(0,0,0);"></li>
|
||||
<li style="background:rgb(132,0,0);"></li>
|
||||
<li style="background:rgb(0,132,0);"></li>
|
||||
<li style="background:rgb(132,132,0);"></li>
|
||||
<li style="background:rgb(0,0,132);"></li>
|
||||
<li style="background:rgb(132,0,132);"></li>
|
||||
<li style="background:rgb(0,132,132);"></li>
|
||||
<li style="background:rgb(132,132,132);"></li>
|
||||
<li style="background:rgb(198,198,198);"></li>
|
||||
<li style="background:rgb(255,0,0);"></li>
|
||||
<li style="background:rgb(0,255,0);"></li>
|
||||
<li style="background:rgb(255,255,0);"></li>
|
||||
<li style="background:rgb(0,0,255);"></li>
|
||||
<li style="background:rgb(255,0,255);"></li>
|
||||
<li style="background:rgb(0,255,255);"></li>
|
||||
<li style="background:rgb(255,255,255);"></li>
|
||||
</ul>
|
||||
|
||||
<input type="color" id="custom" name="custom" value="#ff0000">
|
||||
</div>
|
||||
<ul class="tools">
|
||||
<li data-tool="paint" class="paint selected"><span class="fa fa-pencil"></span></li>
|
||||
<li data-tool="fill" class="fill"><span class="fa fa-bitbucket"></span></li>
|
||||
<li data-tool="erase" class="erase"><span class="fa fa-eraser"></span></li>
|
||||
<li data-tool="pick" class="pick"><span class="fa fa-eyedropper"></span></li>
|
||||
<li data-tool="lighten" class="lighten"><span class="fa fa-sun-o"></span></li>
|
||||
<li data-tool="darken" class="darken"><span class="fa fa-adjust"></span></li>
|
||||
<li data-tool="trash" class="trash"><span class="fa fa-trash"></span></li>
|
||||
<li data-tool="save" class="save"><span class="fa fa-save"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript" src="//cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/tinycolor.js"></script>
|
||||
<script type="text/javascript" src="/static/paint.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
body {
|
||||
background:#333;
|
||||
padding:20px;
|
||||
font-family:Arial, Verdana, Sans-Serif;
|
||||
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAaUlEQVQYV33Q0Q3AIAgEUBjBFVyBFRzbWVjBEajXBIOVypcJj1NhETG61BiDVJX4Bh211v5hRDiniV+Elx0wQwd0hEatlUop65srMSah23vf8Auz65AWMc8rDHvCCjAQK2KeDcuQDzh+AHEJX8mbbU1BAAAAAElFTkSuQmCC) repeat;
|
||||
}
|
||||
|
||||
.icons {
|
||||
position:absolute;
|
||||
margin:0;
|
||||
padding:20px;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
.icons li {
|
||||
margin:20px;
|
||||
padding:0;
|
||||
list-style:none;
|
||||
padding-top:80px;
|
||||
width:100px;
|
||||
}
|
||||
|
||||
.icons li span {
|
||||
background:#FFF;
|
||||
color:#000;
|
||||
border:1px solid #000;
|
||||
line-height:20px;
|
||||
padding:5px 10px;
|
||||
text-align:center;
|
||||
font-size:10px;
|
||||
line-height:10px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
#palette ul, #palette li {
|
||||
margin:0;padding:0;list-style:none;
|
||||
}
|
||||
|
||||
#palette {
|
||||
list-style:none;
|
||||
position:relative;
|
||||
height: 122px;
|
||||
padding:0 8px;
|
||||
}
|
||||
|
||||
#palette ul {
|
||||
display:block;
|
||||
width:456px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#palette li, #palette input {
|
||||
border: 2px outset;
|
||||
width:49px;
|
||||
height:49px;
|
||||
float:left;
|
||||
display:block;
|
||||
margin:2px;
|
||||
}
|
||||
|
||||
#palette input {
|
||||
width:110px;
|
||||
height:110px;
|
||||
}
|
||||
|
||||
.window {
|
||||
width: 976px;
|
||||
position: relative;
|
||||
background: #0E071A;
|
||||
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.tools {
|
||||
margin:0;padding:0;list-style:none;
|
||||
clear:both;
|
||||
display:block;
|
||||
position:absolute;
|
||||
top: 50px;
|
||||
right: 8px;
|
||||
width: 98px;
|
||||
background:#999999;
|
||||
font-size:0;
|
||||
}
|
||||
.tools span {
|
||||
line-height:30px;
|
||||
}
|
||||
|
||||
.tools li {
|
||||
font-size:16px;
|
||||
width: 45px;
|
||||
height: 40px;
|
||||
text-align:center;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:inline-block;
|
||||
line-height:40px;
|
||||
border:2px outset #EEEEEE;
|
||||
background:#F5F5F5;
|
||||
cursor:pointer;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
.tools li.selected {
|
||||
background:#000;
|
||||
color:#FFF;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #FFF;
|
||||
background: #6D38BB;
|
||||
height:40px;
|
||||
margin:0;
|
||||
padding:0 8px;
|
||||
line-height:40px;
|
||||
font-weight:normal;
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
table {
|
||||
clear:both;
|
||||
cursor:pointer;
|
||||
margin:10px;
|
||||
border:1px solid #333;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
table td {
|
||||
width:14px;
|
||||
height:14px;
|
||||
border:1px solid #333;
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
'use strict';
|
||||
|
||||
var md = false;
|
||||
var color = tinycolor('#840000');
|
||||
var update;
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
var picker = $('#custom');
|
||||
var palette = $('#palette');
|
||||
|
||||
picker.val(color.toHexString());
|
||||
|
||||
$(document)
|
||||
.on('mousedown',function(e){md=true;})
|
||||
.on('mouseup',function(e){md=false;});
|
||||
|
||||
$('table').on('dragstart', function(e){
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
for (var y = 0; y < 11; y++) {
|
||||
var row = $('<tr></tr>');
|
||||
for (var x = 0; x < 53; x++) {
|
||||
row.append('<td></td>');
|
||||
}
|
||||
$('tbody').append(row);
|
||||
}
|
||||
|
||||
$('.tools li').on('click', function(){
|
||||
switch($(this).index()){
|
||||
case 6:
|
||||
clear();
|
||||
break;
|
||||
case 7:
|
||||
save();
|
||||
break;
|
||||
default:
|
||||
$('.tools li').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
picker.on('change', function(){
|
||||
color = tinycolor($(this).val());
|
||||
})
|
||||
|
||||
palette.find('li').on('click', function(){
|
||||
pick(this);
|
||||
});
|
||||
|
||||
function handle_tool(obj, is_click){
|
||||
switch($('.tools li.selected').index()){
|
||||
case 0: //'paint':
|
||||
paint(obj);
|
||||
break;
|
||||
case 1: // Fill
|
||||
if( is_click ) fill(obj);
|
||||
break;
|
||||
case 2: // Erase
|
||||
update_pixel(obj, tinycolor('#000000'));
|
||||
break;
|
||||
case 3: //'pick':
|
||||
pick(obj);
|
||||
break;
|
||||
case 4: //'lighten':
|
||||
lighten(obj);
|
||||
break;
|
||||
case 5: //'darken':
|
||||
darken(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var fill_target = null;
|
||||
var fill_stack = [];
|
||||
function fill(obj){
|
||||
fill_target = tinycolor($(obj).css('background-color')).toRgbString();
|
||||
|
||||
if( fill_target == color.toRgbString() ){
|
||||
return false;
|
||||
}
|
||||
|
||||
var x = $(obj).index();
|
||||
var y = $(obj).parent().index();
|
||||
|
||||
socket.send("fill");
|
||||
socket.send(new Uint8Array([x, y, color.toRgb().r, color.toRgb().g, color.toRgb().b]));
|
||||
socket.send('show');
|
||||
|
||||
do_fill(obj);
|
||||
|
||||
while(fill_stack.length > 0){
|
||||
var pixel = fill_stack.pop();
|
||||
do_fill(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
function is_target_color(obj){
|
||||
return ( tinycolor($(obj).css('background-color')).toRgbString() == fill_target);
|
||||
}
|
||||
|
||||
function do_fill(obj){
|
||||
var obj = $(obj);
|
||||
|
||||
if( is_target_color(obj) ){
|
||||
|
||||
$(obj).css('background-color', color.toRgbString());
|
||||
|
||||
var r = obj.next('td'); // Right
|
||||
var l = obj.prev('td'); // Left
|
||||
var u = obj.parent().prev('tr').find('td:eq(' + obj.index() + ')'); // Above
|
||||
var d = obj.parent().next('tr').find('td:eq(' + obj.index() + ')'); // Below
|
||||
|
||||
if( r.length && is_target_color(r[0]) ) fill_stack.push(r[0]);
|
||||
if( l.length && is_target_color(l[0]) ) fill_stack.push(l[0]);
|
||||
if( u.length && is_target_color(u[0]) ) fill_stack.push(u[0]);
|
||||
if( d.length && is_target_color(d[0]) ) fill_stack.push(d[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function save(){
|
||||
var filename = prompt('Please enter a filename', 'mypaint');
|
||||
filename = filename.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
socket.send('save');
|
||||
socket.send(filename);
|
||||
}
|
||||
|
||||
function clear(){
|
||||
$('td').css('background-color','rgb(0,0,0)').data('changed',false);
|
||||
socket.send('clear');
|
||||
socket.send('show');
|
||||
}
|
||||
|
||||
function lighten(obj){
|
||||
var c = tinycolor($(obj).css('background-color'));
|
||||
c.lighten(5);
|
||||
update_pixel(obj, c);
|
||||
}
|
||||
|
||||
function darken(obj){
|
||||
var c = tinycolor($(obj).css('background-color'));
|
||||
c.darken(5);
|
||||
update_pixel(obj, c);
|
||||
}
|
||||
|
||||
function pick(obj){
|
||||
color = tinycolor($(obj).css('background-color'));
|
||||
picker.val(color.toHexString());
|
||||
}
|
||||
|
||||
function update_pixel(obj, col){
|
||||
var bgcol = tinycolor($(obj).css('background-color'));
|
||||
|
||||
if(col != bgcol){
|
||||
$(obj)
|
||||
.data('changed', true)
|
||||
.css('background-color', col.toRgbString());
|
||||
}
|
||||
}
|
||||
|
||||
function update_pixels(){
|
||||
var changed = false;
|
||||
|
||||
$('td').each(function( index, obj ){
|
||||
if($(obj).data('changed')){
|
||||
$(obj).data('changed',false);
|
||||
changed = true;
|
||||
|
||||
var x = $(this).index();
|
||||
var y = $(this).parent().index();
|
||||
var col = tinycolor($(obj).css('background-color')).toRgb();
|
||||
|
||||
if(socket) {
|
||||
socket.send(new Uint8Array([x, y, col.r, col.g, col.b]));
|
||||
}
|
||||
}
|
||||
});
|
||||
if(changed){
|
||||
socket.send('show');
|
||||
}
|
||||
}
|
||||
|
||||
function paint(obj){
|
||||
update_pixel(obj, color);
|
||||
}
|
||||
|
||||
$('table td').on('click', function(){
|
||||
handle_tool(this, true);
|
||||
});
|
||||
$('table td').on('mousemove', function(){
|
||||
if(!md) return false;
|
||||
handle_tool(this, false);
|
||||
})
|
||||
|
||||
const socket = new WebSocket('ws://' + window.location.host + '/paint');
|
||||
socket.addEventListener('message', ev => {
|
||||
console.log('<<< ' + ev.data);
|
||||
|
||||
if(ev.data.substring(0, 6) == "alert:") {
|
||||
alert(ev.data.substring(6));
|
||||
}
|
||||
});
|
||||
socket.addEventListener('close', ev => {
|
||||
console.log('<<< closed');
|
||||
});
|
||||
|
||||
socket.addEventListener('open', ev => {
|
||||
clear();
|
||||
update = setInterval(update_pixels, 50);
|
||||
});
|
||||
});
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,142 @@
|
|||
import time
|
||||
import random
|
||||
import math
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
blob_count = 10
|
||||
|
||||
|
||||
class Blob():
|
||||
def __init__(self):
|
||||
self.x = float(random.randint(0, width - 1))
|
||||
self.y = float(random.randint(0, height - 1))
|
||||
self.r = (float(random.randint(0, 40)) / 10.0) + 5.0
|
||||
self.dx = (float(random.randint(0, 2)) / 10.0) - 0.1
|
||||
self.dy = (float(random.randint(0, 2)) / 10.0) - 0.05 # positive bias
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup_portrait():
|
||||
global width, height, liquid, blobs
|
||||
width = GalacticUnicorn.HEIGHT
|
||||
height = GalacticUnicorn.WIDTH
|
||||
liquid = [[0.0 for y in range(height)] for x in range(width)]
|
||||
blobs = [Blob() for i in range(blob_count)]
|
||||
|
||||
|
||||
hue = 0.0
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return graphics.create_pen(int(v), int(t), int(p))
|
||||
if i == 1:
|
||||
return graphics.create_pen(int(q), int(v), int(p))
|
||||
if i == 2:
|
||||
return graphics.create_pen(int(p), int(v), int(t))
|
||||
if i == 3:
|
||||
return graphics.create_pen(int(p), int(q), int(v))
|
||||
if i == 4:
|
||||
return graphics.create_pen(int(t), int(p), int(v))
|
||||
if i == 5:
|
||||
return graphics.create_pen(int(v), int(p), int(q))
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def update_liquid():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
liquid[x][y] = 0.0
|
||||
|
||||
for blob in blobs:
|
||||
r_sq = blob.r * blob.r
|
||||
blob_y_range = range(max(math.floor(blob.y - blob.r), 0),
|
||||
min(math.ceil(blob.y + blob.r), height))
|
||||
blob_x_range = range(max(math.floor(blob.x - blob.r), 0),
|
||||
min(math.ceil(blob.x + blob.r), width))
|
||||
|
||||
for y in blob_y_range:
|
||||
for x in blob_x_range:
|
||||
x_diff = x - blob.x
|
||||
y_diff = y - blob.y
|
||||
d_sq = x_diff * x_diff + y_diff * y_diff
|
||||
if d_sq <= r_sq:
|
||||
liquid[x][y] += 1.0 - (d_sq / r_sq)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def move_blobs():
|
||||
for blob in blobs:
|
||||
blob.x += blob.dx
|
||||
blob.y += blob.dy
|
||||
|
||||
if blob.x < 0.0 or blob.x >= float(width):
|
||||
blob.dx = 0.0 - blob.dx
|
||||
|
||||
if blob.y < 0.0 or blob.y >= float(height):
|
||||
blob.dy = 0.0 - blob.dy
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw_portrait():
|
||||
global hue
|
||||
hue += 0.001
|
||||
|
||||
dark = from_hsv(hue, 1.0, 0.3)
|
||||
mid = from_hsv(hue, 1.0, 0.6)
|
||||
bright = from_hsv(hue, 1.0, 1.0)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
v = liquid[x][y]
|
||||
|
||||
# select a colour for this pixel based on how much
|
||||
# "blobfluence" there is at this position in the liquid
|
||||
if v >= 1.5:
|
||||
graphics.set_pen(bright)
|
||||
elif v >= 1.25:
|
||||
graphics.set_pen(mid)
|
||||
elif v >= 1.0:
|
||||
graphics.set_pen(dark)
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(y, x)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
setup_portrait()
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
setup_portrait()
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
update_liquid()
|
||||
move_blobs()
|
||||
draw_portrait()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,111 @@
|
|||
import time
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
|
||||
c64 = [
|
||||
" ",
|
||||
" ",
|
||||
" OOOOO OOOOOO OO OOOO OO OO XXXXXXX ",
|
||||
" OO OO OO OOOO OO OO OO OO XXXXXXX ",
|
||||
" OO OO OO OO OO OO OO OO OO XXXXXXX ",
|
||||
" OOOOO OOOO OOOOOO OO OO OOOO XXXXXXX ",
|
||||
" OOOO OO OO OO OO OO OO XXXXXXX ",
|
||||
" OO OO OO OO OO OO OO OO OO XXXXXXX ",
|
||||
" OO OO OOOOOO OO OO OOOO OO OO XXXXXXX ",
|
||||
" XXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_C64 = (230, 210, 250)
|
||||
BACKGROUND_C64 = (20, 20, 120)
|
||||
|
||||
spectrum = [
|
||||
" ",
|
||||
" ",
|
||||
" O OOOO OOOO OOOOO O O O O XXXXXXXX ",
|
||||
" O O O O O O O O O O O X XXXXXX ",
|
||||
" O O O O O O O X XXXXXX ",
|
||||
" O O O OOOOOO O O X XXXXXX ",
|
||||
" O O O O O O O X XXXXXX ",
|
||||
" OOOOOO OOOO O O OOOOO X XXXXXX ",
|
||||
" X X ",
|
||||
" XXXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_SPECTRUM = (0, 0, 0)
|
||||
BACKGROUND_SPECTRUM = (180, 150, 150)
|
||||
|
||||
bbc_micro = [
|
||||
" ",
|
||||
" ",
|
||||
" OOOOO OO OOOO OOO OOOO O ",
|
||||
" O O O O O O O O O O ",
|
||||
" O O O O O O O O ",
|
||||
" OOOOO O O OOOO O O O ",
|
||||
" O O OOOOOO O O O O ",
|
||||
" O O O O O O O O O O ",
|
||||
" OOOOO O O OOOO OOO OOOO O ",
|
||||
" XXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_BBC_MICRO = (255, 255, 255)
|
||||
BACKGROUND_BBC_MICRO = (0, 0, 0)
|
||||
|
||||
PROMPT_C64 = 0
|
||||
PROMPT_SPECTRUM = 1
|
||||
PROMPT_BBC_MICRO = 2
|
||||
prompt = 0
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw(image, fg, bg, time_ms):
|
||||
fg_pen = graphics.create_pen(fg[0], fg[1], fg[2])
|
||||
bg_pen = graphics.create_pen(bg[0], bg[1], bg[2])
|
||||
for y in range(len(image)):
|
||||
row = image[y]
|
||||
for x in range(len(row)):
|
||||
pixel = row[x]
|
||||
# draw the prompt text
|
||||
if pixel == 'O':
|
||||
graphics.set_pen(fg_pen)
|
||||
|
||||
# draw the caret blinking
|
||||
elif pixel == 'X' and (time_ms // 300) % 2:
|
||||
graphics.set_pen(fg_pen)
|
||||
|
||||
else:
|
||||
graphics.set_pen(bg_pen)
|
||||
|
||||
graphics.pixel(x, y)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
prompt = (time_ms // 3000) % 3
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
if prompt == PROMPT_C64:
|
||||
draw(c64, FOREGROUND_C64, BACKGROUND_C64, time_ms)
|
||||
|
||||
elif prompt == PROMPT_SPECTRUM:
|
||||
draw(spectrum, FOREGROUND_SPECTRUM, BACKGROUND_SPECTRUM, time_ms)
|
||||
|
||||
elif prompt == PROMPT_BBC_MICRO:
|
||||
draw(bbc_micro, FOREGROUND_BBC_MICRO, BACKGROUND_BBC_MICRO, time_ms)
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,106 @@
|
|||
import time
|
||||
import math
|
||||
from galactic import GalacticUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
|
||||
|
||||
gu = GalacticUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = GalacticUnicorn.WIDTH
|
||||
height = GalacticUnicorn.HEIGHT
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return int(v), int(t), int(p)
|
||||
if i == 1:
|
||||
return int(q), int(v), int(p)
|
||||
if i == 2:
|
||||
return int(p), int(v), int(t)
|
||||
if i == 3:
|
||||
return int(p), int(q), int(v)
|
||||
if i == 4:
|
||||
return int(t), int(p), int(v)
|
||||
if i == 5:
|
||||
return int(v), int(p), int(q)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
global hue_offset, phase
|
||||
phase_percent = phase / 15
|
||||
for x in range(width):
|
||||
colour = hue_map[int((x + (hue_offset * width)) % width)]
|
||||
for y in range(height):
|
||||
v = ((math.sin((x + y) / stripe_width + phase_percent) + 1.5) / 2.5)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(colour[0] * v), int(colour[1] * v), int(colour[2] * v)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
gu.update(graphics)
|
||||
|
||||
|
||||
hue_map = [from_hsv(x / width, 1.0, 1.0) for x in range(width)]
|
||||
hue_offset = 0.0
|
||||
|
||||
animate = True
|
||||
stripe_width = 3.0
|
||||
speed = 1.0
|
||||
|
||||
gu.set_brightness(0.5)
|
||||
|
||||
phase = 0
|
||||
while True:
|
||||
|
||||
if animate:
|
||||
phase += speed
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
|
||||
hue_offset += 0.01
|
||||
hue_offset = 1.0 if hue_offset > 1.0 else hue_offset
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
|
||||
hue_offset -= 0.01
|
||||
hue_offset = 0.0 if hue_offset < 0.0 else hue_offset
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
gu.adjust_brightness(+0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
gu.adjust_brightness(-0.01)
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
|
||||
animate = False
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_A):
|
||||
speed += 0.05
|
||||
speed = 10.0 if speed > 10.0 else speed
|
||||
animate = True
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_B):
|
||||
speed -= 0.05
|
||||
speed = 0.0 if speed < 0.0 else speed
|
||||
animate = True
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_C):
|
||||
stripe_width += 0.05
|
||||
stripe_width = 10.0 if stripe_width > 10.0 else stripe_width
|
||||
|
||||
if gu.is_pressed(GalacticUnicorn.SWITCH_D):
|
||||
stripe_width -= 0.05
|
||||
stripe_width = 1.0 if stripe_width < 1.0 else stripe_width
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
draw()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,130 @@
|
|||
#include "galactic_unicorn.h"
|
||||
|
||||
|
||||
/***** Methods *****/
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(Channel___del___obj, Channel___del__);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(Channel_configure_obj, 1, Channel_configure);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(Channel_restore_obj, Channel_restore);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_waveforms_obj, 1, 2, Channel_waveforms);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_frequency_obj, 1, 2, Channel_frequency);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_volume_obj, 1, 2, Channel_volume);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_attack_duration_obj, 1, 2, Channel_attack_duration);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_decay_duration_obj, 1, 2, Channel_decay_duration);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_sustain_level_obj, 1, 2, Channel_sustain_level);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_release_duration_obj, 1, 2, Channel_release_duration);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_pulse_width_obj, 1, 2, Channel_pulse_width);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(Channel_trigger_attack_obj, Channel_trigger_attack);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(Channel_trigger_release_obj, Channel_trigger_release);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(Channel_play_tone_obj, 2, Channel_play_tone);
|
||||
//MP_DEFINE_CONST_FUN_OBJ_1(Channel_stop_playing_obj, Channel_stop_playing);
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn___del___obj, GalacticUnicorn___del__);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_clear_obj, GalacticUnicorn_clear);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_update_obj, GalacticUnicorn_update);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_set_brightness_obj, GalacticUnicorn_set_brightness);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_get_brightness_obj, GalacticUnicorn_get_brightness);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_adjust_brightness_obj, GalacticUnicorn_adjust_brightness);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_set_volume_obj, GalacticUnicorn_set_volume);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_get_volume_obj, GalacticUnicorn_get_volume);
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_adjust_volume_obj, GalacticUnicorn_adjust_volume);
|
||||
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_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_2(GalacticUnicorn_synth_channel_obj, GalacticUnicorn_synth_channel);
|
||||
|
||||
/***** Binding of Methods *****/
|
||||
STATIC const mp_rom_map_elem_t Channel_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Channel___del___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&Channel_configure_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_restore), MP_ROM_PTR(&Channel_restore_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_waveforms), MP_ROM_PTR(&Channel_waveforms_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&Channel_frequency_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_volume), MP_ROM_PTR(&Channel_volume_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_attack_duration), MP_ROM_PTR(&Channel_attack_duration_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_decay_duration), MP_ROM_PTR(&Channel_decay_duration_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sustain_level), MP_ROM_PTR(&Channel_sustain_level_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_release_duration), MP_ROM_PTR(&Channel_release_duration_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pulse_width), MP_ROM_PTR(&Channel_pulse_width_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_trigger_attack), MP_ROM_PTR(&Channel_trigger_attack_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_trigger_release), MP_ROM_PTR(&Channel_trigger_release_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_play_tone), MP_ROM_PTR(&Channel_play_tone_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_NOISE), MP_ROM_INT(128) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SQUARE), MP_ROM_INT(64) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SAW), MP_ROM_INT(32) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TRIANGLE), MP_ROM_INT(16) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SINE), MP_ROM_INT(8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WAVE), MP_ROM_INT(1) },
|
||||
};
|
||||
|
||||
STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&GalacticUnicorn___del___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&GalacticUnicorn_clear_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&GalacticUnicorn_update_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&GalacticUnicorn_set_brightness_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_brightness), MP_ROM_PTR(&GalacticUnicorn_get_brightness_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_adjust_brightness), MP_ROM_PTR(&GalacticUnicorn_adjust_brightness_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_volume), MP_ROM_PTR(&GalacticUnicorn_set_volume_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_volume), MP_ROM_PTR(&GalacticUnicorn_get_volume_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_adjust_volume), MP_ROM_PTR(&GalacticUnicorn_adjust_volume_obj) },
|
||||
{ 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_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_synth_channel), MP_ROM_PTR(&GalacticUnicorn_synth_channel_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(53) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(11) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_A), MP_ROM_INT(0) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_B), MP_ROM_INT(1) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_C), MP_ROM_INT(3) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_D), MP_ROM_INT(6) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_SLEEP), MP_ROM_INT(27) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SWITCH_VOLUME_UP), MP_ROM_INT(7) },
|
||||
{ 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) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(Channel_locals_dict, Channel_locals_dict_table);
|
||||
STATIC MP_DEFINE_CONST_DICT(GalacticUnicorn_locals_dict, GalacticUnicorn_locals_dict_table);
|
||||
|
||||
/***** Class Definition *****/
|
||||
const mp_obj_type_t Channel_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Channel,
|
||||
.print = Channel_print,
|
||||
.make_new = Channel_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&Channel_locals_dict,
|
||||
};
|
||||
|
||||
const mp_obj_type_t GalacticUnicorn_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_GalacticUnicorn,
|
||||
.print = GalacticUnicorn_print,
|
||||
.make_new = GalacticUnicorn_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&GalacticUnicorn_locals_dict,
|
||||
};
|
||||
|
||||
/***** Globals Table *****/
|
||||
STATIC const mp_map_elem_t galactic_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_galactic) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Channel), (mp_obj_t)&Channel_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_GalacticUnicorn), (mp_obj_t)&GalacticUnicorn_type },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_galactic_globals, galactic_globals_table);
|
||||
|
||||
/***** Module Definition *****/
|
||||
const mp_obj_module_t galactic_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_galactic_globals,
|
||||
};
|
||||
#if MICROPY_VERSION <= 70144
|
||||
MP_REGISTER_MODULE(MP_QSTR_galactic, galactic_user_cmodule, MODULE_GALACTIC_ENABLED);
|
||||
#else
|
||||
MP_REGISTER_MODULE(MP_QSTR_galactic, galactic_user_cmodule);
|
||||
#endif
|
|
@ -0,0 +1,516 @@
|
|||
#include "libraries/galactic_unicorn/galactic_unicorn.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "micropython/modules/util.hpp"
|
||||
#include <cstdio>
|
||||
#include <cfloat>
|
||||
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
extern "C" {
|
||||
#include "galactic_unicorn.h"
|
||||
#include "micropython/modules/pimoroni_i2c/pimoroni_i2c.h"
|
||||
#include "py/builtin.h"
|
||||
|
||||
|
||||
/********** Channel **********/
|
||||
|
||||
/***** Variables Struct *****/
|
||||
typedef struct _Channel_obj_t {
|
||||
mp_obj_base_t base;
|
||||
AudioChannel* channel;
|
||||
} _Channel_obj_t;
|
||||
|
||||
|
||||
/***** Print *****/
|
||||
void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind; //Unused input parameter
|
||||
//_Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t);
|
||||
//AudioChannel* channel = self->channel;
|
||||
mp_print_str(print, "Channel(");
|
||||
mp_print_str(print, ")");
|
||||
}
|
||||
|
||||
|
||||
/***** Constructor *****/
|
||||
mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, "Cannot create Channel objects. They can only be accessed from GalacticUnicorn.synth_channel()");
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
/***** Destructor ******/
|
||||
mp_obj_t Channel___del__(mp_obj_t self_in) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
/***** Helper Functions *****/
|
||||
void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) {
|
||||
int waveforms = mp_obj_get_int(in);
|
||||
const int mask = (NOISE | SQUARE | SAW | TRIANGLE | SINE | WAVE);
|
||||
if(waveforms < 0 || (waveforms & mask) == 0) {
|
||||
mp_raise_ValueError("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE");
|
||||
}
|
||||
channel.waveforms = (uint8_t)waveforms;
|
||||
}
|
||||
|
||||
void set_channel_frequency(AudioChannel& channel, mp_obj_t in) {
|
||||
int freq = mp_obj_get_int(in);
|
||||
if(freq <= 0 || freq > UINT16_MAX) {
|
||||
mp_raise_ValueError("frequency out of range. Expected greater than 0Hz to 65535Hz");
|
||||
}
|
||||
channel.frequency = (uint16_t)freq;
|
||||
}
|
||||
|
||||
void set_channel_volume(AudioChannel& channel, mp_obj_t in) {
|
||||
float volume = mp_obj_get_float(in);
|
||||
if(volume < 0.0f || volume > 1.0f) {
|
||||
mp_raise_ValueError("volume out of range. Expected 0.0 to 1.0");
|
||||
}
|
||||
channel.volume = (uint16_t)(volume * UINT16_MAX);
|
||||
}
|
||||
|
||||
void set_channel_attack(AudioChannel& channel, mp_obj_t in) {
|
||||
int attack_ms = (int)(mp_obj_get_float(in) * 1000.0f);
|
||||
if(attack_ms < 0 || attack_ms > UINT16_MAX) {
|
||||
mp_raise_ValueError("attack out of range. Expected 0.0s to 65.5s");
|
||||
}
|
||||
channel.attack_ms = MAX(attack_ms, 1);
|
||||
}
|
||||
|
||||
void set_channel_decay(AudioChannel& channel, mp_obj_t in) {
|
||||
int decay_ms = (int)(mp_obj_get_float(in) * 1000.0f);
|
||||
if(decay_ms < 0 || decay_ms > UINT16_MAX) {
|
||||
mp_raise_ValueError("decay out of range. Expected 0.0s to 65.5s");
|
||||
}
|
||||
channel.decay_ms = MAX(decay_ms, 1);
|
||||
}
|
||||
|
||||
void set_channel_sustain(AudioChannel& channel, mp_obj_t in) {
|
||||
float sustain = mp_obj_get_float(in);
|
||||
if(sustain < 0.0f || sustain > 1.0f) {
|
||||
mp_raise_ValueError("sustain out of range. Expected 0.0 to 1.0");
|
||||
}
|
||||
channel.sustain = (uint16_t)(sustain * UINT16_MAX);
|
||||
}
|
||||
|
||||
void set_channel_release(AudioChannel& channel, mp_obj_t in) {
|
||||
int release_ms = (int)(mp_obj_get_float(in) * 1000.0f);
|
||||
if(release_ms < 0 || release_ms > UINT16_MAX) {
|
||||
mp_raise_ValueError("release out of range. Expected 0.0s to 65.5s");
|
||||
}
|
||||
channel.release_ms = MAX(release_ms, 1);
|
||||
}
|
||||
|
||||
void set_channel_pulse_width(AudioChannel& channel, mp_obj_t in) {
|
||||
float pulse_width = mp_obj_get_float(in);
|
||||
if(pulse_width < 0.0f || pulse_width > 1.0f) {
|
||||
mp_raise_ValueError("pulse_width out of range. Expected 0.0 to 1.0");
|
||||
}
|
||||
channel.pulse_width = (uint16_t)(pulse_width * UINT16_MAX);
|
||||
}
|
||||
|
||||
|
||||
/***** Methods *****/
|
||||
mp_obj_t Channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_self, ARG_waveforms, ARG_frequency, ARG_volume, ARG_attack, ARG_decay, ARG_sustain, ARG_release, ARG_pulse_width };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_waveforms, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_frequency, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_volume, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_attack, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_decay, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_sustain, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_pulse_width, MP_ARG_OBJ, {.u_obj = mp_const_none} }
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Channel_obj_t);
|
||||
|
||||
mp_obj_t waveforms = args[ARG_waveforms].u_obj;
|
||||
if(waveforms != mp_const_none) {
|
||||
set_channel_waveforms(*self->channel, waveforms);
|
||||
}
|
||||
|
||||
mp_obj_t frequency = args[ARG_frequency].u_obj;
|
||||
if(frequency != mp_const_none) {
|
||||
set_channel_frequency(*self->channel, frequency);
|
||||
}
|
||||
|
||||
mp_obj_t volume = args[ARG_volume].u_obj;
|
||||
if(volume != mp_const_none) {
|
||||
set_channel_volume(*self->channel, volume);
|
||||
}
|
||||
|
||||
mp_obj_t attack = args[ARG_attack].u_obj;
|
||||
if(attack != mp_const_none) {
|
||||
set_channel_attack(*self->channel, attack);
|
||||
}
|
||||
|
||||
mp_obj_t decay = args[ARG_decay].u_obj;
|
||||
if(decay != mp_const_none) {
|
||||
set_channel_decay(*self->channel, decay);
|
||||
}
|
||||
|
||||
mp_obj_t sustain = args[ARG_sustain].u_obj;
|
||||
if(sustain != mp_const_none) {
|
||||
set_channel_sustain(*self->channel, sustain);
|
||||
}
|
||||
|
||||
mp_obj_t release = args[ARG_release].u_obj;
|
||||
if(release != mp_const_none) {
|
||||
set_channel_release(*self->channel, release);
|
||||
}
|
||||
|
||||
mp_obj_t pulse_width = args[ARG_pulse_width].u_obj;
|
||||
if(pulse_width != mp_const_none) {
|
||||
set_channel_pulse_width(*self->channel, pulse_width);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_restore(mp_obj_t self_in) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t);
|
||||
self->channel->restore();
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_waveforms(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_int(self->channel->waveforms);
|
||||
}
|
||||
|
||||
set_channel_waveforms(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_frequency(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_int(self->channel->frequency);
|
||||
}
|
||||
|
||||
set_channel_frequency(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_volume(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->volume / UINT16_MAX);
|
||||
}
|
||||
|
||||
set_channel_volume(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_attack_duration(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->attack_ms / 1000.0f);
|
||||
}
|
||||
|
||||
set_channel_attack(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_decay_duration(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->decay_ms / 1000.0f);
|
||||
}
|
||||
|
||||
set_channel_decay(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_sustain_level(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->sustain / UINT16_MAX);
|
||||
}
|
||||
|
||||
set_channel_sustain(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_release_duration(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->release_ms / 1000.0f);
|
||||
}
|
||||
|
||||
set_channel_release(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_pulse_width(size_t n_args, const mp_obj_t *args) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t);
|
||||
|
||||
if(n_args == 1) {
|
||||
return mp_obj_new_float((float)self->channel->pulse_width / 0xffff);
|
||||
}
|
||||
|
||||
set_channel_pulse_width(*self->channel, args[1]);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_trigger_attack(mp_obj_t self_in) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t);
|
||||
self->channel->trigger_attack();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_trigger_release(mp_obj_t self_in) {
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t);
|
||||
self->channel->trigger_release();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_self, ARG_freq, ARG_volume, ARG_fade_in, ARG_fade_out };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_frequency, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_volume, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_attack, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
_Channel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Channel_obj_t);
|
||||
|
||||
set_channel_frequency(*self->channel, args[ARG_freq].u_obj);
|
||||
|
||||
mp_obj_t volume = args[ARG_volume].u_obj;
|
||||
if(volume != mp_const_none) {
|
||||
set_channel_volume(*self->channel, volume);
|
||||
}
|
||||
else {
|
||||
self->channel->volume = UINT16_MAX;
|
||||
}
|
||||
|
||||
mp_obj_t attack_ms = args[ARG_fade_in].u_obj;
|
||||
if(attack_ms != mp_const_none) {
|
||||
set_channel_attack(*self->channel, attack_ms);
|
||||
}
|
||||
else {
|
||||
self->channel->attack_ms = 1;
|
||||
}
|
||||
|
||||
mp_obj_t release_ms = args[ARG_fade_out].u_obj;
|
||||
if(release_ms != mp_const_none) {
|
||||
set_channel_release(*self->channel, release_ms);
|
||||
}
|
||||
else {
|
||||
self->channel->release_ms = 1;
|
||||
}
|
||||
|
||||
self->channel->waveforms = Waveform::SINE;
|
||||
self->channel->decay_ms = 1;
|
||||
self->channel->sustain = UINT16_MAX;
|
||||
|
||||
self->channel->trigger_attack();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
/********** GalacticUnicorn **********/
|
||||
|
||||
/***** Variables Struct *****/
|
||||
typedef struct _GalacticUnicorn_obj_t {
|
||||
mp_obj_base_t base;
|
||||
GalacticUnicorn* galactic;
|
||||
} _GalacticUnicorn_obj_t;
|
||||
|
||||
typedef struct _ModPicoGraphics_obj_t {
|
||||
mp_obj_base_t base;
|
||||
PicoGraphics *graphics;
|
||||
DisplayDriver *display;
|
||||
void *spritedata;
|
||||
void *buffer;
|
||||
_PimoroniI2C_obj_t *i2c;
|
||||
//mp_obj_t scanline_callback; // Not really feasible in MicroPython
|
||||
} ModPicoGraphics_obj_t;
|
||||
|
||||
/***** Print *****/
|
||||
void GalacticUnicorn_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind; //Unused input parameter
|
||||
//_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
mp_print_str(print, "GalacticUnicorn()");
|
||||
}
|
||||
|
||||
|
||||
/***** Constructor *****/
|
||||
mp_obj_t GalacticUnicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
_GalacticUnicorn_obj_t *self = nullptr;
|
||||
|
||||
enum { ARG_pio, ARG_sm, ARG_pins, ARG_common_pin, ARG_direction, ARG_counts_per_rev, ARG_count_microsteps, ARG_freq_divider };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_pio, MP_ARG_INT },
|
||||
{ MP_QSTR_sm, MP_ARG_INT }
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
int pio_int = args[ARG_pio].u_int;
|
||||
if(pio_int < 0 || pio_int > (int)NUM_PIOS) {
|
||||
mp_raise_ValueError("pio out of range. Expected 0 to 1");
|
||||
}
|
||||
//PIO pio = pio_int == 0 ? pio0 : pio1;
|
||||
|
||||
int sm = args[ARG_sm].u_int;
|
||||
if(sm < 0 || sm > (int)NUM_PIO_STATE_MACHINES) {
|
||||
mp_raise_ValueError("sm out of range. Expected 0 to 3");
|
||||
}
|
||||
|
||||
|
||||
GalacticUnicorn *galactic = m_new_class(GalacticUnicorn);
|
||||
galactic->init();
|
||||
|
||||
self = m_new_obj_with_finaliser(_GalacticUnicorn_obj_t);
|
||||
self->base.type = &GalacticUnicorn_type;
|
||||
self->galactic = galactic;
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
|
||||
/***** Destructor ******/
|
||||
mp_obj_t GalacticUnicorn___del__(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
m_del_class(GalacticUnicorn, self->galactic);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
/***** Methods *****/
|
||||
extern mp_obj_t GalacticUnicorn_clear(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->clear();
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_update(mp_obj_t self_in, mp_obj_t graphics_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(graphics_in, ModPicoGraphics_obj_t);
|
||||
|
||||
self->galactic->update(picographics->graphics);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_set_brightness(mp_obj_t self_in, mp_obj_t value) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->set_brightness(mp_obj_get_float(value));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_get_brightness(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
return mp_obj_new_float(self->galactic->get_brightness());
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_adjust_brightness(mp_obj_t self_in, mp_obj_t delta) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->adjust_brightness(mp_obj_get_float(delta));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_set_volume(mp_obj_t self_in, mp_obj_t value) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->set_volume(mp_obj_get_float(value));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_get_volume(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
return mp_obj_new_float(self->galactic->get_volume());
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_adjust_volume(mp_obj_t self_in, mp_obj_t delta) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->adjust_volume(mp_obj_get_float(delta));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_light(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
return mp_obj_new_float(self->galactic->light());
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_is_pressed(mp_obj_t self_in, mp_obj_t button) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
return mp_obj_new_bool(self->galactic->is_pressed((uint8_t)mp_obj_get_int(button)));
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_RW);
|
||||
if(bufinfo.len < 1) {
|
||||
mp_raise_ValueError("Supplied buffer is too small!");
|
||||
}
|
||||
|
||||
self->galactic->play_sample((uint8_t *)bufinfo.buf, bufinfo.len);
|
||||
|
||||
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();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
self->galactic->stop_playing();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_synth_channel(mp_obj_t self_in, mp_obj_t channel_in) {
|
||||
_GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t);
|
||||
|
||||
// Check that the channel is valid
|
||||
int channel = mp_obj_get_int(channel_in);
|
||||
if(channel < 0 || channel >= (int)PicoSynth::CHANNEL_COUNT) {
|
||||
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("channel out of range. Expected 0 to %d"), PicoSynth::CHANNEL_COUNT - 1);
|
||||
}
|
||||
|
||||
// NOTE This seems to work, in that it give MP access to the calibration object
|
||||
// Could very easily mess up in weird ways once object deletion is considered?
|
||||
_Channel_obj_t *channel_obj = m_new_obj_with_finaliser(_Channel_obj_t);
|
||||
channel_obj->base.type = &Channel_type;
|
||||
channel_obj->channel = &self->galactic->synth_channel(channel);
|
||||
|
||||
return MP_OBJ_FROM_PTR(channel_obj);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Include MicroPython API.
|
||||
#include "py/runtime.h"
|
||||
|
||||
/***** Extern of Class Definition *****/
|
||||
extern const mp_obj_type_t Channel_type;
|
||||
extern const mp_obj_type_t GalacticUnicorn_type;
|
||||
|
||||
/***** Extern of Class Methods *****/
|
||||
extern void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
|
||||
extern mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
|
||||
extern mp_obj_t Channel___del__(mp_obj_t self_in);
|
||||
extern mp_obj_t Channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
||||
extern mp_obj_t Channel_restore(mp_obj_t self_in);
|
||||
extern mp_obj_t Channel_waveforms(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_frequency(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_volume(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_attack_duration(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_decay_duration(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_sustain_level(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_release_duration(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_pulse_width(size_t n_args, const mp_obj_t *args);
|
||||
extern mp_obj_t Channel_trigger_attack(mp_obj_t self_in);
|
||||
extern mp_obj_t Channel_trigger_release(mp_obj_t self_in);
|
||||
extern mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
||||
extern mp_obj_t Channel_stop_playing(mp_obj_t self_in);
|
||||
|
||||
extern void GalacticUnicorn_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
|
||||
extern mp_obj_t GalacticUnicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
|
||||
extern mp_obj_t GalacticUnicorn___del__(mp_obj_t self_in);
|
||||
extern mp_obj_t GalacticUnicorn_clear(mp_obj_t self_in);
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_update(mp_obj_t self_in, mp_obj_t graphics_in);
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_set_brightness(mp_obj_t self_in, mp_obj_t value);
|
||||
extern mp_obj_t GalacticUnicorn_get_brightness(mp_obj_t self_in);
|
||||
extern mp_obj_t GalacticUnicorn_adjust_brightness(mp_obj_t self_in, mp_obj_t delta);
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_set_volume(mp_obj_t self_in, mp_obj_t value);
|
||||
extern mp_obj_t GalacticUnicorn_get_volume(mp_obj_t self_in);
|
||||
extern mp_obj_t GalacticUnicorn_adjust_volume(mp_obj_t self_in, mp_obj_t delta);
|
||||
|
||||
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_synth(mp_obj_t self_in);
|
||||
extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in);
|
||||
|
||||
extern mp_obj_t GalacticUnicorn_synth_channel(mp_obj_t self_in, mp_obj_t channel_in);
|
|
@ -0,0 +1,24 @@
|
|||
set(MOD_NAME galactic_unicorn)
|
||||
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
|
||||
add_library(usermod_${MOD_NAME} INTERFACE)
|
||||
|
||||
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)
|
||||
pico_generate_pio_header(usermod_${MOD_NAME} ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/galactic_unicorn/audio_i2s.pio)
|
||||
|
||||
target_include_directories(usermod_${MOD_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/
|
||||
)
|
||||
|
||||
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
|
||||
MODULE_GALACTIC_ENABLED=1
|
||||
)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})
|
|
@ -44,6 +44,8 @@ include(pcf85063a/micropython)
|
|||
|
||||
include(picographics/micropython)
|
||||
include(jpegdec/micropython)
|
||||
include(galactic_unicorn/micropython)
|
||||
|
||||
|
||||
include(modules_py/modules_py)
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
include_directories(${CMAKE_CURRENT_LIST_DIR}/../../)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../")
|
||||
|
||||
# Essential
|
||||
include(pimoroni_i2c/micropython)
|
||||
include(pimoroni_bus/micropython)
|
||||
|
||||
# Pico Graphics Essential
|
||||
include(hershey_fonts/micropython)
|
||||
include(bitmap_fonts/micropython)
|
||||
include(picographics/micropython)
|
||||
|
||||
# Pico Graphics Extra
|
||||
include(jpegdec/micropython)
|
||||
include(qrcode/micropython/micropython)
|
||||
|
||||
# Sensors & Breakouts
|
||||
include(breakout_dotmatrix/micropython)
|
||||
include(breakout_encoder/micropython)
|
||||
include(breakout_ioexpander/micropython)
|
||||
include(breakout_ltr559/micropython)
|
||||
include(breakout_as7262/micropython)
|
||||
include(breakout_rgbmatrix5x5/micropython)
|
||||
include(breakout_matrix11x7/micropython)
|
||||
include(breakout_msa301/micropython)
|
||||
include(breakout_pmw3901/micropython)
|
||||
include(breakout_mics6814/micropython)
|
||||
include(breakout_potentiometer/micropython)
|
||||
include(breakout_rtc/micropython)
|
||||
include(breakout_trackball/micropython)
|
||||
include(breakout_sgp30/micropython)
|
||||
include(breakout_bh1745/micropython)
|
||||
include(breakout_bme68x/micropython)
|
||||
include(breakout_bme280/micropython)
|
||||
include(breakout_bmp280/micropython)
|
||||
include(breakout_icp10125/micropython)
|
||||
include(breakout_scd41/micropython)
|
||||
include(breakout_vl53l5cx/micropython)
|
||||
include(pcf85063a/micropython)
|
||||
|
||||
# Utility
|
||||
include(adcfft/micropython)
|
||||
|
||||
# LEDs & Matrices
|
||||
include(galactic_unicorn/micropython)
|
||||
|
||||
# include(micropython-common)
|
||||
|
||||
include(modules_py/modules_py)
|
|
@ -18,6 +18,7 @@ target_sources(usermod_${MOD_NAME} INTERFACE
|
|||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p8.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb332.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb565.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb888.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -125,12 +125,14 @@ STATIC const mp_map_elem_t picographics_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_PACK), MP_ROM_INT(DISPLAY_INKY_PACK) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME), MP_ROM_INT(DISPLAY_INKY_FRAME) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_4), MP_ROM_INT(DISPLAY_INKY_FRAME_4) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_GALACTIC_UNICORN), MP_ROM_INT(DISPLAY_GALACTIC_UNICORN) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_P8), MP_ROM_INT(PEN_P8) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_RGB332), MP_ROM_INT(PEN_RGB332) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_RGB565), MP_ROM_INT(PEN_RGB565) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PEN_RGB888), MP_ROM_INT(PEN_RGB888) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_picographics_globals, picographics_globals_table);
|
||||
|
||||
|
|
|
@ -112,6 +112,14 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
|
|||
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
|
||||
if(pen_type == -1) pen_type = PEN_P4;
|
||||
break;
|
||||
case DISPLAY_GALACTIC_UNICORN:
|
||||
width = 53;
|
||||
height = 11;
|
||||
bus_type = BUS_PIO;
|
||||
// Portrait to match labelling
|
||||
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
|
||||
if(pen_type == -1) pen_type = PEN_RGB888;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -132,6 +140,8 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h
|
|||
return PicoGraphics_PenRGB332::buffer_size(width, height);
|
||||
case PEN_RGB565:
|
||||
return PicoGraphics_PenRGB565::buffer_size(width, height);
|
||||
case PEN_RGB888:
|
||||
return PicoGraphics_PenRGB888::buffer_size(width, height);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -223,7 +233,10 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
|
|||
} else if (display == DISPLAY_INKY_PACK) {
|
||||
self->display = m_new_class(UC8151, width, height, (Rotation)rotate, spi_bus);
|
||||
|
||||
} else {
|
||||
} else if (display == DISPLAY_GALACTIC_UNICORN) {
|
||||
self->display = m_new_class(DisplayDriver, width, height, (Rotation)rotate);
|
||||
}
|
||||
else {
|
||||
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, round, spi_bus);
|
||||
}
|
||||
|
||||
|
@ -267,6 +280,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
|
|||
case PEN_RGB565:
|
||||
self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer);
|
||||
break;
|
||||
case PEN_RGB888:
|
||||
self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ enum PicoGraphicsDisplay {
|
|||
DISPLAY_I2C_OLED_128X128,
|
||||
DISPLAY_INKY_PACK,
|
||||
DISPLAY_INKY_FRAME,
|
||||
DISPLAY_INKY_FRAME_4
|
||||
DISPLAY_INKY_FRAME_4,
|
||||
DISPLAY_GALACTIC_UNICORN
|
||||
};
|
||||
|
||||
enum PicoGraphicsPenType {
|
||||
|
@ -23,13 +24,15 @@ enum PicoGraphicsPenType {
|
|||
PEN_P4,
|
||||
PEN_P8,
|
||||
PEN_RGB332,
|
||||
PEN_RGB565
|
||||
PEN_RGB565,
|
||||
PEN_RGB888
|
||||
};
|
||||
|
||||
enum PicoGraphicsBusType {
|
||||
BUS_I2C,
|
||||
BUS_SPI,
|
||||
BUS_PARALLEL
|
||||
BUS_PARALLEL,
|
||||
BUS_PIO
|
||||
};
|
||||
|
||||
// Type
|
||||
|
|
Ładowanie…
Reference in New Issue