1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-28 23:49:20 +00:00
8bitworkshop/presets/c64/musicplayer.c

269 lines
22 KiB
C
Raw Normal View History

/*
A simple music player.
*/
#include "common.h"
//#link "common.c"
2023-11-03 19:54:27 +00:00
#include "sidmacros.h"
#include <cbm_petscii_charmap.h>
2023-11-20 21:29:51 +00:00
#include <6502.h>
2023-11-03 19:54:27 +00:00
// SID frequency table (PAL version)
const int note_table_pal[96] = {
278,295,313,331,351,372,394,417,442,468,496,526,557,590,625,662,702,743,788,834,884,937,992,1051,1114,1180,1250,1325,1403,1487,1575,1669,1768,1873,1985,2103,2228,2360,2500,2649,2807,2973,3150,3338,3536,3746,3969,4205,4455,4720,5001,5298,5613,5947,6300,6675,7072,7493,7938,8410,8910,9440,10001,10596,11226,11894,12601,13350,14144,14985,15876,16820,17820,18880,20003,21192,22452,23787,25202,26700,28288,29970,31752,33640L,35641L,37760L,40005L,42384L,44904L,47574L,50403L,53401L,56576L,59940L,63504L,67280L
};
#define NOTE_TABLE note_table_pal
void sid_init() {
2022-08-11 20:27:20 +00:00
SID.amp = 0x0f; // volume 15, no filters
}
// music player logic
byte music_index = 0;
byte cur_duration = 0;
2023-11-03 19:54:27 +00:00
byte music_wavebits = 0;
const byte music1[]; // music data -- see end of file
const byte* music_ptr = music1;
byte next_music_byte() {
return *music_ptr++;
}
void play_music() {
static byte chs = 0;
if (music_ptr) {
// run out duration timer yet?
while (cur_duration == 0) {
// fetch next byte in score
byte note = next_music_byte();
// is this a note?
if ((note & 0x80) == 0) {
int period = NOTE_TABLE[note & 63];
// see which pulse generator is free
if (!(chs & 1)) {
2023-11-03 19:54:27 +00:00
SID_STOP(v1, music_wavebits);
SID_FREQ(v1, period);
SID_START(v1, music_wavebits);
chs |= 1;
} else if (!(chs & 2)) {
2023-11-03 19:54:27 +00:00
SID_STOP(v2, music_wavebits);
SID_FREQ(v2, period);
SID_START(v2, music_wavebits);
chs |= 2;
} else if (!(chs & 4)) {
2023-11-03 19:54:27 +00:00
SID_STOP(v3, music_wavebits);
SID_FREQ(v3, period);
SID_START(v3, music_wavebits);
chs |= 4;
}
} else {
// end of score marker
if (note == 0xff)
music_ptr = NULL;
// set duration until next note
cur_duration = note & 63;
// reset channel used mask
chs = 0;
}
}
cur_duration--;
}
}
void start_music(const byte* music) {
music_ptr = music;
cur_duration = 0;
}
2023-11-03 19:54:27 +00:00
#ifdef __MAIN__
typedef struct Param {
const char* const name;
word index;
word mask;
byte lshift;
word low;
word high;
} Param;
#define NPARAMS 21
const Param SID_PARAMS[NPARAMS] = {
{ "Main Volume", 0x18, 0x0f, 0, 0, 15 },
{ "Pulse Width", 0x03, 0x0f, 0, 0, 15 },
{ "Env Attack", 0x05, 0xf0, 4, 0, 15 },
{ "Env Decay", 0x05, 0x0f, 0, 0, 15 },
{ "Env Sustain", 0x06, 0xf0, 4, 0, 15 },
{ "Env Release", 0x06, 0x0f, 0, 0, 15 },
{ "Noise", 0x04, 0x80, 7, 0, 1 },
{ "Pulse", 0x04, 0x40, 6, 0, 1 },
{ "Sawtooth", 0x04, 0x20, 5, 0, 1 },
{ "Triangle", 0x04, 0x10, 4, 0, 1 },
{ "Ring Mod", 0x04, 0x04, 2, 0, 1 },
{ "Sync", 0x04, 0x02, 1, 0, 1 },
{ "Voice 3 Off", 0x18, 0x80, 7, 0, 1 },
//{ "Filter Freq", 0x15, 0xffff, 5, 0, 0x7ff },
{ "Filter Frequency", 0x16, 0xff, 0, 0, 0xff },
{ "Filter Resonance", 0x17, 0xf0, 4, 0, 15 },
{ "Filter Voice 1", 0x17, 0x1, 0, 0, 1 },
{ "Filter Voice 2", 0x17, 0x2, 1, 0, 1 },
{ "Filter Voice 3", 0x17, 0x4, 2, 0, 1 },
{ "Filter Lowpass", 0x18, 0x10, 4, 0, 1 },
{ "Filter Bandpass", 0x18, 0x20, 5, 0, 1 },
{ "Filter Highpass", 0x18, 0x40, 6, 0, 1 },
};
int paramValues[NPARAMS];
int currentParam;
void drawValue(char i) {
char buf[16];
gotoxy(19, i+2);
cputs(i == currentParam ? ">" : " ");
gotoxy(20, i+2);
cclear(8);
gotoxy(20, i+2);
itoa(paramValues[i], buf, 16);
cputs(buf);
if (SID_PARAMS[i].high < 16) {
cputc(' ');
chline(paramValues[i]);
cclear(16 - paramValues[i]);
}
}
void drawParams() {
char i;
clrscr();
cputsxy(0, 0, "SID Parameters:");
for (i = 0; i < NPARAMS; ++i) {
const Param* param = &SID_PARAMS[i];
gotoxy(1, i+2);
puts(param->name);
drawValue(i);
}
}
2023-11-20 21:29:51 +00:00
void setSIDRegisters() {
2023-11-03 19:54:27 +00:00
char i;
word val;
char buf[30];
memset(buf, 0, sizeof(buf));
for (i = 0; i < NPARAMS; ++i) {
const Param* param = &SID_PARAMS[i];
val = PEEKW(buf + param->index);
val &= ~param->mask;
val |= (paramValues[i] << param->lshift) & param->mask;
if (param->mask & 0xff00) {
POKEW(buf + param->index, val);
} else {
POKE(buf + param->index, val);
}
}
//memcpy((void*)0xd400, buf, sizeof(buf));
memcpy((void*)0xd415, buf+0x15, 8);
POKE(0xd403, buf[0x03]);
POKE(0xd405, buf[0x05]);
POKE(0xd406, buf[0x06]);
POKE(0xd40a, buf[0x03]);
POKE(0xd40c, buf[0x05]);
POKE(0xd40d, buf[0x06]);
POKE(0xd411, buf[0x03]);
POKE(0xd413, buf[0x05]);
POKE(0xd414, buf[0x06]);
music_wavebits = buf[0x04];
}
2023-11-20 21:29:51 +00:00
char music_update() {
if (!music_ptr) start_music(music1);
play_music();
return IRQ_NOT_HANDLED;
2023-11-03 19:54:27 +00:00
}
void handleInput() {
char key = 0;
char joy = joy_read(0);
2023-11-20 21:29:51 +00:00
if (joy == 0) return;
2023-11-03 19:54:27 +00:00
if (JOY_UP(joy)) key = 'i';
if (JOY_DOWN(joy)) key = 'k';
if (JOY_LEFT(joy)) key = 'j';
if (JOY_RIGHT(joy)) key = 'l';
2023-11-20 21:29:51 +00:00
switch (key) {
case 'i': // UP
if (currentParam > 0) {
--currentParam;
drawValue(currentParam+1);
drawValue(currentParam);
}
break;
case 'k': // DOWN
if (currentParam < NPARAMS - 1) {
++currentParam;
drawValue(currentParam-1);
drawValue(currentParam);
}
break;
case 'j': // LEFT
if (paramValues[currentParam] > SID_PARAMS[currentParam].low) {
paramValues[currentParam]--;
drawValue(currentParam);
setSIDRegisters();
}
break;
case 'l': // RIGHT
if (paramValues[currentParam] < SID_PARAMS[currentParam].high) {
paramValues[currentParam]++;
drawValue(currentParam);
setSIDRegisters();
}
break;
}
// delay a few frames to slow down movement
waitvsync();
waitvsync();
waitvsync();
2023-11-03 19:54:27 +00:00
}
void main(void)
{
2023-11-03 19:54:27 +00:00
joy_install (joy_static_stddrv);
2023-11-20 21:29:51 +00:00
// set initial SID parameters
2023-11-03 19:54:27 +00:00
paramValues[0] = 15;
paramValues[1] = 8;
paramValues[2] = 8;
paramValues[3] = 8;
paramValues[4] = 4;
paramValues[5] = 4;
paramValues[7] = 1; // pulse
2023-11-20 21:29:51 +00:00
setSIDRegisters();
// draw the UI
2023-11-03 19:54:27 +00:00
drawParams();
2023-11-20 21:29:51 +00:00
// set IRQ routine called every frame
set_irq(music_update, (void*)0x9f00, 0x100);
// main loop to handle UI
music_ptr = 0;
while (1) {
2023-11-20 21:29:51 +00:00
waitvsync();
2023-11-03 19:54:27 +00:00
handleInput();
}
}
//
// MUSIC DATA -- "The Easy Winners" by Scott Joplin
//
const byte music1[] = {
0x2a,0x1e,0x95,0x33,0x27,0x9f,0x31,0x25,0x8a,0x2f,0x23,0x8b,0x2e,0x22,0x89,0x2c,0x20,0x8b,0x2a,0x1e,0x95,0x28,0x1c,0x8a,0x27,0x1b,0x95,0x25,0x19,0x8b,0x23,0x17,0x89,0x22,0x12,0x8b,0x23,0x8a,0x25,0x8b,0x27,0x89,0x28,0x1e,0x8c,0x2a,0x1c,0x89,0x2c,0x1b,0x8c,0x2e,0x19,0x89,0x2f,0x17,0x95,0x27,0x23,0x1e,0x95,0x12,0x95,0x23,0x1e,0x27,0x95,0x2f,0x33,0x17,0x95,0x2f,0x33,0x27,0x94,0x12,0x95,0x33,0x36,0x23,0x95,0x38,0x32,0x17,0x8b,0x33,0x36,0x89,0x1e,0x27,0x23,0x8b,0x38,0x32,0x8a,0x33,0x36,0x12,0x8b,0x32,0x38,0x89,0x36,0x33,0x27,0x95,0x3d,0x37,0x10,0x8b,0x3b,0x38,0x8a,0x20,0x23,0x28,0x8b,0x37,0x3d,0x89,0x38,0x3b,0x10,0x8b,0x37,0x3d,0x8a,0x3b,0x38,0x28,0x8b,0x36,0x33,0x8a,0x17,0x95,0x2a,0x1e,0x23,0x8b,0x2c,0x89,0x2e,0x12,0x8b,0x2f,0x8a,0x31,0x27,0x23,0x8b,0x32,0x89,0x2f,0x33,0x17,0x95,0x33,0x2f,0x1e,0x95,0x12,0x94,0x36,0x33,0x1e,0x95,0x32,0x38,0x17,0x8b,0x36,0x33,0x89,0x23,0x1e,0x27,0x8b,0x38,0x32,0x8a,0x33,0x36,0x18,0x8b,0x33,0x36,0x8a,0x33,0x36,0x2a,0x95,0x36,0x19,0x8b,0x31,0x2e,0x89,0x25,0x22,0x2a,0x8b,0x36,0x8a,0x2f,0x35,0x19,0x8b,0x36,0x89,0x38,0x2f,0x29,0x95,0x2e,0x36,0x1e,0x95,0x1c,0x28,0x95,0x27,0x1b,0x8b,0x2a,0x8a,0x2e,0x25,0x19,0x8b,0x34,0x89,0x33,0x2f,0x23,0x95,0x33,0x2f,0x1e,0x95,0x12,0x94,0x33,0x36,0x23,0x95,0x32,0x38,0x17,0x8b,0x33,0x36,0x8a,0x23,0x27,0x1e,0x8b,0x38,0x32,0x89,0x36,0x33,0x12,0x8b,0x32,0x38,0x8a,0x33,0x36,0x1e,0x94,0x37,0x3d,0x10,0x8b,0x3b,0x38,0x8a,0x23,0x28,0x20,0x8b,0x3d,0x37,0x89,0x3b,0x38,0x1c,0x8b,0x38,0x3b,0x8a,0x3a,0x3d,0x38,0x8b,0x3f,0x37,0x3a,0x89,0x27,0x1b,0x8b,0x33,0x8a,0x37,0x22,0x16,0x8b,0x3a,0x8a,0x3f,0x0f,0x1b,0x95,0x3a,0x8b,0x3b,0x8a,0x3d,0x37,0x1c,0x8b,0x3b,0x38,0x89,0x23,0x28,0x20,0x8b,0x3d,0x37,0x8a,0x38,0x3b,0x10,0x8b,0x3d,0x37,0x89,0x38,0x3b,0x20,0x8b,0x36,0x33,0x8a,0x17,0x23,0x8b,0x38,0x34,0x8a,0x33,0x36,0x1e,0x8b,0x31,0x34,0x8a,0x2f,0x33,0x17,0x8b,0x36,0x33,0x8a,0x23,0x27,0x1e,0x8b,0x33,0x36,0x8a,0x36,0x30,0x12,0x8a,0x34,0x31,0x8a,0x1e,0x28,0x22,0x8b,0x30,0x36,0x89,0x34,0x31,0x12,0x8b,0x2e,0x28,0x33,0x8a,0x31,0x28,0x2e,0x95,0x27,0x2f,0x1e,0x95,0x12,0x95,0x14,0x94,0x16,0x95,0x2f,0x33,0x17,0x95,0x2f,0x33,0x27,0x94,0x12,0x95,0x36,0x33,0x23,0x95,0x32,0x38,0x17,0x8a,0x33,0x36,0x8a,0x23,0x27,0x1e,0x8b,0x32,0x38,0x8a,0x33,0x36,0x12,0x8b,0x38,0x32,0x8a,0x36,0x33,0x27,0x95,0x3d,0x37,0x10,0x8a,0x3b,0x38,0x8a,0x23,0x28,0x20,0x8b,0x37,0x3d,0x89,0x3b,0x38,0x10,0x8b,0x37,0x3d,0x8a,0x38,0x3b,0x23,0x8b,0x36,0x33,0x8a,0x17,0x95,0x2a,0x1e,0x23,0x8b,0x2c,0x8a,0x2e,0x12,0x8b,0x2f,0x89,0x31,0x23,0x1e,0x8b,0x32,0x8a,0x33,0x2f,0x17,0x95,0x2f,0x33,0x1e,0x94,0x12,0x95,0x36,0x33,0x27,0x95,0x38,0x32,0x17,0x8a,0x36,0x33,0x8a,0x23,0x1e,0x27,0x8b,0x38,0x32,0x89,0x33,0x36,0x18,0x8c,0x36,0x33,0x89,0x33,0x36,0x21,0x95,0x36,0x19,0x8b,0x2e,0x31,0x8a,0x25,0x2a,0x22,0x8b,0x36,0x89,0x35,0x2f,0x19,0x8b,0x36,0x8a,0x2f,0x38,0x25,0x94,0x36,0x2e,0x2a,0x95,0x1c,0x28,0x95,0x27,0x1b,0x8c,0x2a,0x89,0x2e,0x25,0x19,0x8b,0x34,0x8a,0x33,0x2f,0x23,0x95,0x2f,0x33,0x1e,0x94,0x12,0x95,0x33,0x36,0x23,0x95,0x32,0x38,0x17,0x8a,0x33,0x36,0x8a,0x23,0x1e,0x27,0x8b,0x32,0x38,0x89,0x36,0x33,0x12,0x8c,0x32,0x38,0x89,0x33,0x36,0x1e,0x95,0x37,0x3d,0x10,0x8b,0x3b,0x38,0x8a,0x23,0x20,0x28,0x8a,0x3d,0x37,0x8a,0x38,0x3b,0x1c,0x8b,0x3b,0x38,0x8a,0x3d,0x38,0x3a,0x8b,0x3a,0x3f,0x37,0x89,0x27,0x1b,0x8b,0x33,0x8a,0x37,0x22,0x16,0x8b,0x3a,0x8a,0x3f,0x0f,0x1b,0x95,0x3a,0x8b,0x3b,0x8a,0x3d,0x37,0x1c,0x8b,0x3b,0x38,0x8a,0x20,0x28,0x23,0x8b,0x3d,0x37,0x89,0x38,0x3b,0x10,0x8b,0x3d,0x37,0x8a,0x38,0x3b,0x20,0x8b,0x33,0x36,0x89,0x17,0x23,0x8b,0x38,0x34,0x8a,0x36,0x33,0x23,0x8b,0x34,0x31,0x89,0x2f,0x33,0x17,0x8c,0x36,0x33,0x89,0x1e,0x27,0x23,0x8c,0x33,0x36,0x89,0x36,0x30,0x12,0x8b,0x34,0x31,0x8a,0x1e,0x28,0x22,0x8a,0x30,0x36,0x8a,0x34,0x31,0x1e,0x8b,0x28,0x2e,0x33,0x89,0x28,0x31,0x2e,0x95,0x27,0x2f,0x17,0x95,0x12,0x1e,0x95,0x3b,0x36,0x33,0x96,0x2a,0x8b,0x2b,0x8a,0x2c,0x1e,0x12,0x8b,0x2d,0x89,0x2e,0x28,0x1e,0x8b,0x2e,0x31,0x8a,0x16,0x22,0x8b,0x36,0x89,0x34,0x1e,0x22,0x8b,0x31,0x8a,0x2c,0x25,0x19,0x8b,0x2d,0x89,0x2e,0x28,0x25,0x8b,0x33,0x2e,0x8a,0x25,0x19,0x8c,0x31,0x89,0x2c,0x1a,0x26,0x8b,0x2e,0x8a,0x2f,0x1b,0x27,0x8b,0x2a,0x89,0x2c,0x23,0x1e,0x8b,0x2e,0x8a,0x2f,
};
2023-11-03 19:54:27 +00:00
#endif