blob: f39396a7dc7203335e839657270939e0ccd786a6 [file] [log] [blame] [edit]
#include <string>
#include <cstring>
#include <memory>
#include <cstdlib>
#include "piano.hpp"
using namespace blit;
uint16_t music_tick = 0;
uint16_t beat = 0;
constexpr int step_count = 30;
constexpr int channel_count = 1;
int16_t notes[channel_count][step_count];
int16_t note_y[41];
int16_t selected_note = 0;
int16_t selected_channel = 0;
bool playing = false;
int note_to_freq(int note) {
return powf(2.0f, (float(note) / 12.0f)) * 293.67f;
}
/* setup */
void init() {
set_screen_mode(hires);
for(int channel = 0; channel < channel_count; channel++) {
for(int beat = 0; beat < step_count; beat++) {
notes[channel][beat] = 12;
}
}
// configure voices
// melody track
channels[0].waveforms = Waveform::SQUARE;
channels[0].attack_ms = 10;
channels[0].decay_ms = 350;
channels[0].sustain = 0;
channels[0].release_ms = 168;
// rhythm track
channels[1].waveforms = Waveform::SINE;
channels[1].attack_ms = 5;
channels[1].decay_ms = 100;
channels[1].sustain = 0;
channels[1].release_ms = 0;
// drum track
channels[2].waveforms = 0; // Waveform::NOISE;
channels[2].attack_ms = 10;
channels[2].decay_ms = 750;
channels[2].sustain = 0;
channels[2].release_ms = 100;
// set global volume
// volume = 2048;
screen.pen = Pen(0, 0, 0, 255);
screen.clear();
}
void render(uint32_t time_ms) {
screen.pen = Pen(20, 30, 40);
screen.clear();
for(int pass = 0; pass < 2; pass++) {
for(int note = 0; note < 41; note++) {
// White: 0, 2, 4, 5, 7, 9, 11
// Black: 1, 3, 6, 8, 10
int octave = floor(note / 12);
int y = 70 * octave;
int y_offset = 0;
int w = 20;
int h = 9;
switch(note % 12) {
case 11:
y += 10;
case 9:
y += 10;
case 7:
y += 10;
case 5:
y += 10;
case 4:
y += 10;
case 2:
y += 10;
case 0:
y_offset = 3;
if (pass == 1) continue;
screen.pen = Pen(255, 255, 255);
break;
case 3:
y += 10;
case 1:
y += 4;
w = 15;
h = 7;
y_offset = 2;
if (pass == 0) continue;
screen.pen = Pen(0, 0, 0);
break;
case 10:
y += 10;
case 8:
y += 10;
case 6:
y += 34;
w = 15;
h = 7;
y_offset = 2;
if (pass == 0) continue;
screen.pen = Pen(0, 0, 0);
break;
}
y = screen.bounds.h - y - 10;
note_y[note] = y + y_offset;
screen.rectangle(Rect(
0, y,
w, h
));
screen.rectangle(Rect(
20, y,
300, h
));
}
}
for(int x = 0; x < 30; x++) {
screen.pen = x & 1 ? Pen(20, 30, 40, 220) : Pen(30, 40, 50, 220);
screen.rectangle(Rect(
20 + x * 10, 0,
10, 240
));
int note = notes[selected_channel][x] & ~0x100;
bool mute = notes[selected_channel][x] & 0x100;
if(note > -1){
screen.pen = mute ? Pen(255, 255, 255, 100) : Pen(255, 255, 255, 255);
screen.rectangle(Rect(
21 + x * 10, note_y[note],
8, 3
));
}
}
screen.pen = Pen(255, 200, 200);
screen.rectangle(Rect(
20 + beat * 10, note_y[notes[selected_channel][beat] & ~0x100] - 1,
10, 5
));
screen.pen = Pen(255, 0, 0);
screen.rectangle(Rect(
20 + selected_note * 10, note_y[notes[selected_channel][selected_note] & ~0x100] - 1,
10, 5
));
screen.watermark();
}
void play_selected_note() {
if(playing) return;
int16_t hz = note_to_freq(notes[selected_channel][selected_note] & ~0x100);
channels[selected_channel].frequency = hz;
channels[selected_channel].trigger_attack();
}
void play() {
if(!playing) return;
static uint16_t prev_beat = 1;
beat = (music_tick / 25) % step_count; // 200ms per beat
music_tick++;
if (beat == prev_beat) return;
prev_beat = beat;
for(uint8_t i = 0; i < channel_count; i++) {
if(notes[i][beat] & 0x100) {
channels[i].trigger_release();
} else {
int16_t hz = note_to_freq(notes[i][beat] & ~0x100);
channels[i].frequency = hz;
channels[i].trigger_attack();
}
}
}
void update(uint32_t time_ms) {
play();
if(buttons.pressed & Button::DPAD_LEFT) {
if(selected_note > 0){
selected_note--;
play_selected_note();
}
}
if(buttons.pressed & Button::DPAD_RIGHT) {
if(selected_note < 30){
selected_note++;
play_selected_note();
}
}
if(buttons.pressed & Button::DPAD_UP) {
if(notes[selected_channel][selected_note] < 41) {
notes[selected_channel][selected_note]++;
play_selected_note();
}
}
if(buttons.pressed & Button::DPAD_DOWN) {
if(notes[selected_channel][selected_note] > 0) {
notes[selected_channel][selected_note]--;
play_selected_note();
}
}
if(buttons.pressed & Button::A) {
playing = !playing;
}
if(buttons.pressed & Button::B) {
bool mute = notes[0][selected_note] & 0x100;
notes[0][selected_note] &= ~0x100;
if(!mute) {
notes[selected_channel][selected_note] |= 0x100;
}
}
if(buttons.pressed & Button::Y) {
music_tick = 0;
beat = 0;
}
}