scrolling and save/load
This commit is contained in:
32
.vscode/launch.json
vendored
Normal file
32
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "(gdb) Launch",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/tracker/build/tracker",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/tracker",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set Disassembly Flavor to Intel",
|
||||||
|
"text": "-gdb-set disassembly-flavor intel",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,9 +9,10 @@ OBJFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.o)
|
|||||||
DEPFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.d)
|
DEPFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.d)
|
||||||
|
|
||||||
CC := gcc
|
CC := gcc
|
||||||
CFLAGS := -Wall -Wextra
|
CFLAGS := -Wall -Wextra -g
|
||||||
CPPFLAGS := -I src $(shell pkg-config --cflags sdl2)
|
CPPFLAGS := -I src $(shell pkg-config --cflags sdl2)
|
||||||
LDLIBS := $(shell pkg-config --libs sdl2)
|
LDLIBS := $(shell pkg-config --libs sdl2)
|
||||||
|
LDFLAGS := -g
|
||||||
|
|
||||||
.PHONY: $(TARGET) all clean
|
.PHONY: $(TARGET) all clean
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|||||||
BIN
tracker/disk.bin
Normal file
BIN
tracker/disk.bin
Normal file
Binary file not shown.
@@ -94,6 +94,18 @@ void serial_write(char c){
|
|||||||
write(STDOUT_FILENO, &c, 1);
|
write(STDOUT_FILENO, &c, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disk_write(void * data, size_t len){
|
||||||
|
FILE * f = fopen("disk.bin", "wb");
|
||||||
|
fwrite(data, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disk_read(void * data, size_t len){
|
||||||
|
FILE * f = fopen("disk.bin", "rb");
|
||||||
|
fread(data, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
static void audio_callback(void *userdata, Uint8 *stream, int len){
|
static void audio_callback(void *userdata, Uint8 *stream, int len){
|
||||||
AudioState *state = userdata;
|
AudioState *state = userdata;
|
||||||
int16_t *output = (int16_t *)stream;
|
int16_t *output = (int16_t *)stream;
|
||||||
|
|||||||
@@ -3,3 +3,5 @@
|
|||||||
|
|
||||||
void psg_write(uint8_t value);
|
void psg_write(uint8_t value);
|
||||||
void serial_write(char c);
|
void serial_write(char c);
|
||||||
|
void disk_write(void * data, size_t len);
|
||||||
|
void disk_read(void * data, size_t len);
|
||||||
@@ -54,13 +54,11 @@ static void flush_psg(){
|
|||||||
static void process_row(){
|
static void process_row(){
|
||||||
row_t * current = &pattern->rows[row];
|
row_t * current = &pattern->rows[row];
|
||||||
for(uint8_t chan=0; chan<3; chan++){
|
for(uint8_t chan=0; chan<3; chan++){
|
||||||
if(current->channel[chan].note < 12 && current->channel[chan].note >= NR_NOTE_PERIODS+12){
|
if(current->channel[chan].note >= 12 && current->channel[chan].note < NR_NOTE_PERIODS+12){
|
||||||
// Only turn off note if it isnt 0
|
|
||||||
if(current->channel[chan].note != 0)
|
|
||||||
psg_state.channel[chan].vol = 15;
|
|
||||||
}else{
|
|
||||||
psg_state.channel[chan].tone = note_periods[current->channel[chan].note-12];
|
psg_state.channel[chan].tone = note_periods[current->channel[chan].note-12];
|
||||||
psg_state.channel[chan].vol = 15-current->channel[chan].volume;
|
psg_state.channel[chan].vol = 0;
|
||||||
|
}else if(current->channel[chan].note == 0xff){
|
||||||
|
psg_state.channel[chan].vol = 15;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,19 +101,10 @@ void player_main(){
|
|||||||
flush_psg();
|
flush_psg();
|
||||||
ui_init();
|
ui_init();
|
||||||
|
|
||||||
pattern_t p = {
|
pattern_t p = {};
|
||||||
.speed = 12,
|
p.speed = 6;
|
||||||
.rows = {
|
|
||||||
{.channel = {{60, 15}, {0, 0}, {0, 0}, {0, 0}}},
|
|
||||||
{.channel = {{67, 15}, {0, 0}, {0, 0}, {0, 0}}},
|
|
||||||
{.channel = {{69, 15}, {0, 0}, {0, 0}, {0, 0}}},
|
|
||||||
{.channel = {{67, 15}, {0, 0}, {0, 0}, {0, 0}}},
|
|
||||||
{.channel = {{60, 15}, {0, 0}, {0, 0}, {0, 0}}},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pattern = &p;
|
pattern = &p;
|
||||||
ui_set_pattern(&p);
|
|
||||||
|
|
||||||
for(;;){
|
for(;;){
|
||||||
if(music_tick_interrupt_happened){
|
if(music_tick_interrupt_happened){
|
||||||
@@ -126,7 +115,7 @@ void player_main(){
|
|||||||
input_interrupt_happened = 0;
|
input_interrupt_happened = 0;
|
||||||
ui_input(input);
|
ui_input(input);
|
||||||
}
|
}
|
||||||
uint8_t action = do_ui();
|
uint8_t action = do_ui(playing, pattern, row);
|
||||||
if(action != ACTION_NONE){
|
if(action != ACTION_NONE){
|
||||||
if(action == ACTION_PLAY_ONCE){
|
if(action == ACTION_PLAY_ONCE){
|
||||||
tick = 0;
|
tick = 0;
|
||||||
@@ -156,3 +145,11 @@ void player_main(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void save(){
|
||||||
|
disk_write(pattern, sizeof(pattern_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(){
|
||||||
|
disk_read(pattern, sizeof(pattern_t));
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define PSG_CLOCK 1000000
|
#define PSG_CLOCK 1000000
|
||||||
#define PATTERN_ROWS 16
|
#define PATTERN_ROWS 32
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
uint8_t note;
|
uint8_t note;
|
||||||
uint8_t volume;
|
|
||||||
} cell_t;
|
} cell_t;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
@@ -21,3 +20,5 @@ typedef struct{
|
|||||||
void player_main();
|
void player_main();
|
||||||
void music_tick_interrupt();
|
void music_tick_interrupt();
|
||||||
void input_interrupt(char input);
|
void input_interrupt(char input);
|
||||||
|
void save();
|
||||||
|
void load();
|
||||||
117
tracker/src/ui.c
117
tracker/src/ui.c
@@ -11,6 +11,9 @@
|
|||||||
#define TRACK_ROWS 16
|
#define TRACK_ROWS 16
|
||||||
#define CHANNELS 4
|
#define CHANNELS 4
|
||||||
|
|
||||||
|
#define MODE_EDIT 0
|
||||||
|
#define MODE_PLAYING 1
|
||||||
|
|
||||||
static uint8_t dirty = 1;
|
static uint8_t dirty = 1;
|
||||||
static uint8_t clear = 1;
|
static uint8_t clear = 1;
|
||||||
|
|
||||||
@@ -22,6 +25,9 @@ static uint8_t input_octave = 4;
|
|||||||
|
|
||||||
static uint8_t do_action = ACTION_NONE;
|
static uint8_t do_action = ACTION_NONE;
|
||||||
static pattern_t * pattern = NULL;
|
static pattern_t * pattern = NULL;
|
||||||
|
static uint8_t start_row = 0;
|
||||||
|
static uint8_t active_row = 0;
|
||||||
|
static uint8_t mode = MODE_EDIT;
|
||||||
|
|
||||||
|
|
||||||
static void serial_puts(char * s){
|
static void serial_puts(char * s){
|
||||||
@@ -173,7 +179,11 @@ static void draw_cell(uint8_t row, uint8_t channel){
|
|||||||
static void draw_row(uint8_t row){
|
static void draw_row(uint8_t row){
|
||||||
uint8_t channel;
|
uint8_t channel;
|
||||||
|
|
||||||
|
if(row == active_row && mode == MODE_PLAYING){
|
||||||
|
reverse_on();
|
||||||
|
}
|
||||||
print_hex_byte(row);
|
print_hex_byte(row);
|
||||||
|
reverse_off();
|
||||||
serial_puts(" ");
|
serial_puts(" ");
|
||||||
|
|
||||||
for (channel = 0; channel < CHANNELS; channel++) {
|
for (channel = 0; channel < CHANNELS; channel++) {
|
||||||
@@ -199,20 +209,36 @@ static void draw(void){
|
|||||||
cursor_hide();
|
cursor_hide();
|
||||||
|
|
||||||
serial_puts("Z80 TRACKER\r\n");
|
serial_puts("Z80 TRACKER\r\n");
|
||||||
|
|
||||||
serial_puts("O[");
|
serial_puts("O[");
|
||||||
print_hex_digit(input_octave);
|
print_hex_digit(input_octave);
|
||||||
serial_puts("]\r\n");
|
serial_puts("] ");
|
||||||
|
|
||||||
|
if(mode == MODE_EDIT){
|
||||||
|
serial_puts("E ");
|
||||||
|
}else if(mode == MODE_PLAYING){
|
||||||
|
serial_puts("P ");
|
||||||
|
}else{
|
||||||
|
serial_puts("x ");
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_puts("S[");
|
||||||
|
print_hex_byte(pattern->speed);
|
||||||
|
serial_puts("] ");
|
||||||
|
|
||||||
|
serial_puts("\r\n");
|
||||||
|
|
||||||
serial_puts(" CH1 CH2 CH3 NOISE\r\n");
|
serial_puts(" CH1 CH2 CH3 NOISE\r\n");
|
||||||
serial_puts(" ------- ------- ------- -------\r\n");
|
serial_puts(" ------- ------- ------- -------\r\n");
|
||||||
|
|
||||||
for (row = 0; row < TRACK_ROWS; row++) {
|
for (row = start_row; row < TRACK_ROWS+start_row; row++) {
|
||||||
draw_row(row);
|
draw_row(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_puts("\r\n");
|
serial_puts("\r\n");
|
||||||
serial_puts("Arrows: move A-K: notes X: clear Space: off\r\n");
|
serial_puts("Arrows: move A-K: notes X: clear Space: off\r\n");
|
||||||
serial_puts("P: play O: stop Q: quit Numbers: oct\r\n");
|
serial_puts("P: play O: stop Q: quit Numbers: oct\r\n");
|
||||||
|
serial_puts("N: save M: load\r\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep the real terminal cursor out of the tracker area.
|
* Keep the real terminal cursor out of the tracker area.
|
||||||
@@ -223,13 +249,19 @@ static void draw(void){
|
|||||||
static void move_up(void){
|
static void move_up(void){
|
||||||
if (cursor_row > 0)
|
if (cursor_row > 0)
|
||||||
cursor_row--;
|
cursor_row--;
|
||||||
|
while(cursor_row < start_row){
|
||||||
|
start_row -= TRACK_ROWS;
|
||||||
|
}
|
||||||
|
|
||||||
dirty = 1;
|
dirty = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void move_down(void){
|
static void move_down(void){
|
||||||
if (cursor_row < TRACK_ROWS - 1)
|
if (cursor_row < PATTERN_ROWS - 1)
|
||||||
cursor_row++;
|
cursor_row++;
|
||||||
|
while(cursor_row >= start_row+TRACK_ROWS){
|
||||||
|
start_row += TRACK_ROWS;
|
||||||
|
}
|
||||||
|
|
||||||
dirty = 1;
|
dirty = 1;
|
||||||
}
|
}
|
||||||
@@ -288,7 +320,6 @@ static void enter_note(uint8_t note){
|
|||||||
cell_t *cell = &pattern->rows[cursor_row].channel[cursor_channel];
|
cell_t *cell = &pattern->rows[cursor_row].channel[cursor_channel];
|
||||||
|
|
||||||
cell->note = note;
|
cell->note = note;
|
||||||
cell->volume = 15;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Just give every entered note instrument 1 for now.
|
* Just give every entered note instrument 1 for now.
|
||||||
@@ -299,8 +330,7 @@ static void enter_note(uint8_t note){
|
|||||||
/*
|
/*
|
||||||
* Tracker-style behavior: advance after entering a note.
|
* Tracker-style behavior: advance after entering a note.
|
||||||
*/
|
*/
|
||||||
if (cursor_row < TRACK_ROWS - 1)
|
move_down();
|
||||||
cursor_row++;
|
|
||||||
|
|
||||||
dirty = 1;
|
dirty = 1;
|
||||||
}
|
}
|
||||||
@@ -375,6 +405,41 @@ void ui_input(char input){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* , = dec speed
|
||||||
|
*/
|
||||||
|
if (input == ',') {
|
||||||
|
pattern->speed--;
|
||||||
|
dirty = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* . = inc speed
|
||||||
|
*/
|
||||||
|
if (input == '.') {
|
||||||
|
pattern->speed++;
|
||||||
|
dirty = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* N = save
|
||||||
|
*/
|
||||||
|
if (input == 'N' || input == 'n') {
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* M = save
|
||||||
|
*/
|
||||||
|
if (input == 'M' || input == 'm') {
|
||||||
|
load();
|
||||||
|
dirty = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Piano keyboard.
|
* Piano keyboard.
|
||||||
*/
|
*/
|
||||||
@@ -442,7 +507,37 @@ void ui_input(char input){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t do_ui(){
|
uint8_t do_ui(uint8_t _playing, pattern_t * _pattern, uint8_t _row){
|
||||||
|
pattern = _pattern;
|
||||||
|
|
||||||
|
if(_playing){
|
||||||
|
if(mode != MODE_PLAYING){
|
||||||
|
dirty = 1;
|
||||||
|
}
|
||||||
|
mode = MODE_PLAYING;
|
||||||
|
}else{
|
||||||
|
if(mode != MODE_EDIT){
|
||||||
|
dirty = 1;
|
||||||
|
start_row = 0;
|
||||||
|
}
|
||||||
|
mode = MODE_EDIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode == MODE_PLAYING){
|
||||||
|
while(_row>=start_row+TRACK_ROWS){
|
||||||
|
start_row += TRACK_ROWS;
|
||||||
|
dirty = 1;
|
||||||
|
}
|
||||||
|
while(_row<start_row){
|
||||||
|
start_row -= TRACK_ROWS;
|
||||||
|
dirty = 1;
|
||||||
|
}
|
||||||
|
if(_row != active_row){
|
||||||
|
active_row = _row;
|
||||||
|
dirty = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(dirty){
|
if(dirty){
|
||||||
dirty = 0;
|
dirty = 0;
|
||||||
draw();
|
draw();
|
||||||
@@ -455,10 +550,6 @@ uint8_t do_ui(){
|
|||||||
return ACTION_NONE;
|
return ACTION_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_set_pattern(pattern_t * _pattern){
|
|
||||||
pattern = _pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui_init(){
|
void ui_init(){
|
||||||
uint8_t row;
|
uint8_t row;
|
||||||
uint8_t channel;
|
uint8_t channel;
|
||||||
@@ -469,4 +560,8 @@ void ui_init(){
|
|||||||
cursor_row = 0;
|
cursor_row = 0;
|
||||||
cursor_channel = 0;
|
cursor_channel = 0;
|
||||||
input_state = 0;
|
input_state = 0;
|
||||||
|
|
||||||
|
mode = MODE_EDIT;
|
||||||
|
start_row = 0;
|
||||||
|
active_row = 0;
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,5 @@
|
|||||||
#define ACTION_QUIT 3
|
#define ACTION_QUIT 3
|
||||||
|
|
||||||
void ui_input(char input);
|
void ui_input(char input);
|
||||||
uint8_t do_ui();
|
uint8_t do_ui(uint8_t _playing, pattern_t * _pattern, uint8_t _row);
|
||||||
void ui_init();
|
void ui_init();
|
||||||
void ui_set_pattern(pattern_t * _pattern);
|
|
||||||
Reference in New Issue
Block a user