From ae78fca8bf5835ceccdbdc902197fe082b8def30 Mon Sep 17 00:00:00 2001 From: Takamichi Horikawa Date: Wed, 30 Nov 2016 23:01:06 +0900 Subject: added GTK UI --- .gitignore | 11 ++ curses/.gitignore | 11 -- curses/main.c | 59 +++--- fmdriver/fmdriver.h | 29 ++- fmdriver/fmdriver_common.h | 5 +- fmdriver/fmdriver_fmp.c | 275 +++++++++++++++++++++----- fmdriver/fmdriver_fmp.h | 23 ++- fmdsp/fmdsp.c | 252 ++++++++++++++++++++++++ fmdsp/fmdsp.h | 37 ++++ fmdsp/fmdsp_sprites.h | 318 +++++++++++++++++++++++++++++++ gtk/.gitignore | 1 + gtk/Makefile.am | 23 +++ gtk/configure.ac | 10 + gtk/main.c | 466 +++++++++++++++++++++++++++++++++++++++++++++ libopna/opnadrum.h | 2 +- 15 files changed, 1432 insertions(+), 90 deletions(-) create mode 100644 fmdsp/fmdsp.c create mode 100644 fmdsp/fmdsp.h create mode 100644 fmdsp/fmdsp_sprites.h create mode 100644 gtk/.gitignore create mode 100644 gtk/Makefile.am create mode 100644 gtk/configure.ac create mode 100644 gtk/main.c diff --git a/.gitignore b/.gitignore index 729dc37..bbb1194 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ *.o .deps .dirstamp +Makefile.in +aclocal.m4 +autom4te.cache +compile +configure +depcomp +install-sh +missing +Makefile +config.log +config.status diff --git a/curses/.gitignore b/curses/.gitignore index bab26a8..ebac467 100644 --- a/curses/.gitignore +++ b/curses/.gitignore @@ -1,12 +1 @@ -Makefile.in -aclocal.m4 -autom4te.cache -compile -configure -depcomp -install-sh -missing -Makefile -config.log -config.status fmpc diff --git a/curses/main.c b/curses/main.c index bfcaa51..cbde86a 100644 --- a/curses/main.c +++ b/curses/main.c @@ -265,7 +265,7 @@ static bool readrom(struct opna *opna) { const char *home = getenv("HOME"); char *dpath = 0; if (home) { - const char *datadir = "/.local/share/libopna/"; + const char *datadir = "/.local/share/fmplayer/"; dpath = malloc(strlen(home)+strlen(datadir)+strlen(path) + 1); if (dpath) { strcpy(dpath, home); @@ -443,10 +443,11 @@ int main(int argc, char **argv) { work.opna_writereg = opna_writereg_libopna; work.opna_status = opna_status_libopna; work.opna = &timer; - if (!fmp_init(&work, &fmp, g_data, filelen)) { + if (!fmp_load(&fmp, g_data, filelen)) { fprintf(stderr, "not fmp\n"); return 1; } + fmp_init(&work, &fmp); bool pvi_loaded = loadpvi(&work, &fmp, argv[1]); bool ppz_loaded = loadppzpvi(&work, &fmp, argv[1]); @@ -465,18 +466,41 @@ int main(int argc, char **argv) { } SDL_PauseAudioDevice(ad, 0); - setlocale(LC_CTYPE, ""); + + initscr(); + cbreak(); + noecho(); + clear(); + refresh(); + + timeout(20); + + static const char pdzf_mode_str[3][9] = { + "OFF", "STANDARD", "ENHANCED" + }; + mvprintw(0, 0, "PART PTR TONE LEN VOL NOTE DET FREQ PQRAWE"); + mvprintw(14, 61, "PPZ8"); + mvprintw(16, 0, "FM RHYTHM SSG ADPCM"); + mvprintw(17, 0, "TL ENV TL PTR LV LV"); + mvprintw(21, 48, "NZ"); + mvprintw(24, 0, "PPZ: %c%8s PVI: %c%8s PDZF: %s", + ppz_loaded ? ' ' : '!', + fmp.ppz_name, + pvi_loaded ? ' ' : '!', + fmp.pvi_name, + pdzf_mode_str[fmp.pdzf.mode] + ); + enum { - //TBUFLEN = 80*3*2, - TBUFLEN = 0x10000 + TBUFLEN = 80*2*2 }; char titlebuf[TBUFLEN+1] = {0}; - if (work.title) { + for (int l = 0; l < 3; l++) { iconv_t cd = iconv_open("//IGNORE", "CP932"); if (cd != (iconv_t)-1) { char titlebufcrlf[TBUFLEN+1] = {0}; - const char *in = work.title; + const char *in = work.comment[l]; size_t inleft = strlen(in)+1; char *out = titlebufcrlf; size_t outleft = TBUFLEN; @@ -495,28 +519,9 @@ int main(int argc, char **argv) { if (!titlebufcrlf[i]) break; } } + mvprintw(25+l, 0, "%s", titlebuf); } - initscr(); - cbreak(); - noecho(); - clear(); - refresh(); - - timeout(20); - - mvprintw(0, 0, "PART PTR TONE LEN VOL NOTE DET FREQ PQRAWE"); - mvprintw(14, 61, "PPZ8"); - mvprintw(16, 0, "FM RHYTHM SSG ADPCM"); - mvprintw(17, 0, "TL ENV TL PTR LV LV"); - mvprintw(21, 48, "NZ"); - mvprintw(24, 0, "PPZ: %c%8s PVI: %c%8s", - ppz_loaded ? ' ' : '!', - fmp.ppz_name, - pvi_loaded ? ' ' : '!', - fmp.pvi_name); - mvprintw(25, 0, "%s", titlebuf); - int cont = 1; int pause = 0; while (cont) { diff --git a/fmdriver/fmdriver.h b/fmdriver/fmdriver.h index 67557cc..b9946da 100644 --- a/fmdriver/fmdriver.h +++ b/fmdriver/fmdriver.h @@ -5,6 +5,30 @@ #include #include "ppz8.h" +enum { + FMDRIVER_TRACK_NUM = 10, + // 1 line = 80 characters, may contain half-width doublebyte characters + FMDRIVER_TITLE_BUFLEN = 80*2+1, +}; + +enum fmdriver_track_type { + FMDRIVER_TRACK_FM, + FMDRIVER_TRACK_SSG, + FMDRIVER_TRACK_ADPCM, + FMDRIVER_TRACK_PPZ8 +}; + +struct fmdriver_track_status { + bool playing; + enum fmdriver_track_type type; + uint8_t num; + uint8_t ticks; + uint8_t ticks_left; + uint8_t key; + // key after pitchbend, LFO, etc. applied + uint8_t actual_key; +}; + struct fmdriver_work { // set by driver, called by opna void (*driver_opna_interrupt)(struct fmdriver_work *work); @@ -22,8 +46,11 @@ struct fmdriver_work { const struct ppz8_functbl *ppz8_functbl; struct ppz8 *ppz8; - const char *title; + // CP932 encoded + //const char *title; + char comment[3][FMDRIVER_TITLE_BUFLEN]; // driver status + struct fmdriver_track_status track_status[FMDRIVER_TRACK_NUM]; // fm3ex part map }; diff --git a/fmdriver/fmdriver_common.h b/fmdriver/fmdriver_common.h index cd804b8..23600cb 100644 --- a/fmdriver/fmdriver_common.h +++ b/fmdriver/fmdriver_common.h @@ -13,7 +13,10 @@ static inline int16_t u16s16(uint16_t v) { return (v & 0x8000) ? ((int32_t)v)-0x10000 : v; } +#if 0 #include #define FMDRIVER_DEBUG(...) fprintf(stderr, __VA_ARGS__) - +#else +#define FMDRIVER_DEBUG(...) +#endif #endif // MYON_FMDRIVER_COMMON_H_INCLUDED diff --git a/fmdriver/fmdriver_fmp.c b/fmdriver/fmdriver_fmp.c index 76e3798..9195276 100644 --- a/fmdriver/fmdriver_fmp.c +++ b/fmdriver/fmdriver_fmp.c @@ -73,6 +73,41 @@ static uint16_t fmp_fm_freq(uint8_t note) { return freqtab[note%0xc] + ((note/0xc)<<(3+8)); } +static uint8_t fmp_fm_freq2key(uint16_t freq) { + int block = freq >> (8+3); + int f_num = freq & ((1<<(8+3))-1); + if (!f_num) return 0x00; + while (!(f_num & (1<<(8+3-1)))) { + f_num <<= 1; + block--; + } + static const uint16_t freqtab[0xc] = { + 0x042e, // < 9 (a) + 0x046e, + 0x04b1, + 0x04f9, + 0x0544, + 0x0595, + 0x05ea, + 0x0644, + 0x06a3, + 0x0708, + 0x0773, + 0x07e4, + }; + int note = 0; + for (; note < 12; note++) { + if (f_num < freqtab[note]) break; + } + note += 9; + block += (note/12); + note %= 12; + + if (block < 0) return 0x00; + if (block > 8) return 0x8b; + return (block << 4) | note; +} + static uint8_t fmp_ssg_octave(uint8_t note) { return note/0xc; } @@ -116,6 +151,41 @@ static uint16_t fmp_part_ssg_freq(struct fmp_part *part, uint8_t note) { return part->detune + freq; } +static uint8_t fmp_ssg_freq2key(uint16_t freq) { + if (!freq) return 0x00; + int octave = -5; + while (!(freq & 0x8000)) { + freq <<= 1; + octave++; + } + // 7987200.0 / (64*440*(2**((i+2+0.5)/12))/(1<<8)) + static const uint16_t freqtab[0xc] = { + 0xf57f, // > 0 (c) + 0xe7b8, + 0xdab7, + 0xce70, + 0xc2da, + 0xb7ea, + 0xad98, + 0xa3da, + 0x9aa7, + 0x91f9, + 0x89c8, + 0x820c, + }; + int note = 0; + for (; note < 12; note++) { + if (freq > freqtab[note]) break; + } + note += 11; + octave += (note/12); + note %= 12; + + if (octave < 0) return 0x00; + if (octave > 8) return 0x8b; + return (octave << 4) | note; +} + // 3172 static uint16_t fmp_adpcm_freq(uint8_t note) { static const uint16_t freqtab[4][0xc] = { @@ -2466,7 +2536,7 @@ static void fmp_part_cmd(struct fmdriver_work *work, struct driver_fmp *fmp, // 1d26 cmd &= 0x7f; if (cmd >= 0x62) { - if (!fmp_part_cmd_exec2(work, fmp, part, cmd)) return; + if (!fmp_part_cmd_exec2(work, fmp, part, cmd | 0x80)) return; continue; } else { // 1d33 @@ -2476,6 +2546,7 @@ static void fmp_part_cmd(struct fmdriver_work *work, struct driver_fmp *fmp, // 1d36 // note part->tonelen_cnt = len; + part->tonelen = len; if (cmd == 0x61) { // 1d3e part->status.tie = false; @@ -2641,6 +2712,62 @@ static void fmp_part_cmd_rhythm(struct fmdriver_work *work, } } +static uint8_t fmp_note2key(uint8_t note) { + uint8_t octave = note / 0xc; + uint8_t key = note % 0xc; + key |= octave << 4; + return key; +} + +static void fmp_work_status_update(struct fmdriver_work *work, + struct driver_fmp *fmp) { + static const uint8_t fmp_track_map[FMDRIVER_TRACK_NUM] = { + FMP_PART_FM_1, + FMP_PART_FM_2, + FMP_PART_FM_3, + FMP_PART_FM_4, + FMP_PART_FM_5, + FMP_PART_FM_6, + FMP_PART_SSG_1, + FMP_PART_SSG_2, + FMP_PART_SSG_3, + FMP_PART_ADPCM, + }; + + for (int t = 0; t < FMDRIVER_TRACK_NUM; t++) { + struct fmdriver_track_status *track = &work->track_status[t]; + struct fmp_part *part = &fmp->parts[fmp_track_map[t]]; + track->playing = !part->status.off; + track->num = (part->pdzf.mode ? part->pdzf.ppz8_channel : part->opna_keyon_out)+1; + if (part->type.adpcm) { + track->type = FMDRIVER_TRACK_ADPCM; + track->actual_key = 0xff; + } else if (part->type.ssg) { + if (part->u.ssg.env_f.ppz || part->pdzf.mode) { + track->type = FMDRIVER_TRACK_PPZ8; + track->actual_key = 0xff; + } else { + track->type = FMDRIVER_TRACK_SSG; + track->actual_key = part->status.rest ? 0xff : fmp_ssg_freq2key(part->prev_freq); + } + } else { + if (part->pdzf.mode) { + track->type = FMDRIVER_TRACK_PPZ8; + track->actual_key = 0xff; + } else { + track->type = FMDRIVER_TRACK_FM; + track->actual_key = part->status.rest ? 0xff : fmp_fm_freq2key(part->prev_freq); + } + } + if (part->type.fm && part->opna_keyon_out > 3) { + track->num--; + } + track->ticks = part->status.off ? 0 : part->tonelen-1; + track->ticks_left = part->tonelen_cnt; + track->key = part->status.rest ? 0xff : fmp_note2key(part->prev_note); + } +} + // 17f8-1903 static void fmp_timerb(struct fmdriver_work *work, struct driver_fmp *fmp) { // 1805 @@ -2693,6 +2820,7 @@ static void fmp_timerb(struct fmdriver_work *work, struct driver_fmp *fmp) { if (!fmp->rhythm.status) { fmp_part_cmd_rhythm(work, fmp); } + fmp_work_status_update(work, fmp); } static void fmp_init_parts(struct fmdriver_work *work, @@ -2839,7 +2967,7 @@ static void fmp_init_parts(struct fmdriver_work *work, static void fmp_struct_init(struct fmdriver_work *work, struct driver_fmp *fmp) { // TODO - fmp->pdzf.mode = 2; + //fmp->pdzf.mode = 2; // 4e87 fmp->ssg_mix = 0x38; // 3bb7 @@ -2942,26 +3070,97 @@ static void fmp_opna_interrupt(struct fmdriver_work *work) { } } +// copy title string (CP932) to fmdriver_work struct, +// and detect which PDZF(/Z8X) mode to use static void fmp_title(struct fmdriver_work *work, - uint8_t *data, uint16_t datalen, + struct driver_fmp *fmp, uint16_t offset) { - for (unsigned i = 0; ; i++) { - int newline = 0; - if ((offset + i) >= datalen) return; - //if (i > 80*3*2) return; - //if (data[offset+i] == 0x0d) - if (data[offset+i] == 0) break; - /* - if ((data[offset+i] == 0x0d) || (data[offset+i] == 0x0a)){ - data[offset+i] = 0; + int l = 0; + int i = 0; + static const uint8_t pdzf_str[] = "using PDZF"; + const uint8_t *data = fmp->data; + uint16_t datalen = fmp->datalen; + const uint8_t *pdzf_ptr = pdzf_str; + fmp->pdzf.mode = 0; + enum { + STATE_NORMAL, + STATE_ESC, + STATE_CSI, + STATE_SYNC, + } esc_state; + for (int si = 0; ; si++) { + if ((offset + i) >= datalen) { + work->comment[l][0] = 0; + return; + } + if (i > (FMDRIVER_TITLE_BUFLEN-1)) { + return; + } + uint8_t c = data[offset+si]; + + if (l >= 3) { + if (c) { + if (!fmp->pdzf.mode) { + fmp->pdzf.mode = 1; + } + } + return; + } + if (!c) return; + switch (esc_state) { + case STATE_SYNC: + esc_state = STATE_NORMAL; + continue; + case STATE_ESC: + if (c == '[') { + esc_state = STATE_CSI; + } else if (c == '!') { + esc_state = STATE_SYNC; + } else { + esc_state = STATE_NORMAL; + } + continue; + case STATE_CSI: + if (('0' <= c && c <= '9') || c == ';') { + continue; + } else { + esc_state = STATE_NORMAL; + continue; + } + default: + break; + } + // pdzf detection + if (c == *pdzf_ptr++) { + if (!*pdzf_ptr) { + fmp->pdzf.mode = 2; + } + } else { + pdzf_ptr = pdzf_str; + } + + work->comment[l][i] = c; + switch (c) { + case 0: + return; + case '\r': + break; + case '\n': + work->comment[l][i] = 0; + l++; + i = 0; + break; + case 0x1b: + esc_state = STATE_ESC; + break; + default: + i++; break; } - */ } - work->title = data+offset; } -bool fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp, +bool fmp_load(struct driver_fmp *fmp, uint8_t *data, uint16_t datalen) { uint16_t offset = read16le(data); @@ -3179,9 +3378,25 @@ bool fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp, FMDRIVER_DEBUG(" FMTONEPTR: %04X\n", fmp->datainfo.fmtoneptr); FMDRIVER_DEBUG(" SSGTONEPTR: %04X\n", fmp->datainfo.ssgtoneptr); FMDRIVER_DEBUG(" data version: 0x%01X\n", fmp->data_version); + uint16_t pcmptr = read16le(data)-0x12; + if (pcmptr <= datalen && (pcmptr+16) < datalen) { + for (int i = 0; i < 8; i++) { + if (pviname_valid) { + fmp->pvi_name[i] = data[pcmptr+8+i]; + } + if (pviname_valid && fmp->datainfo.flags.ppz) { + fmp->ppz_name[i] = data[pcmptr+0+i]; + } + } + } fmp->bar_tickcnt = fmp->datainfo.bar; fmp->data = data; fmp->datalen = datalen; + return true; +} + +void fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp) { + fmp_title(work, fmp, read16le(fmp->data)+4); fmp_struct_init(work, fmp); fmp_init_parts(work, fmp); uint16_t fmtoneptr = fmp->datainfo.fmtoneptr; @@ -3189,39 +3404,9 @@ bool fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp, fmp->data[fmtoneptr+0x18]&0x7, (fmp->data[fmtoneptr+0x18]>>3)&0x7 ); - for (int i = 0; i < 4; i++) { - static const uint8_t t[4] = { - 0, 2, 1, 3, - }; - FMDRIVER_DEBUG(" %03d %03d %03d %03d %03d %03d %03d %03d %03d\n", - fmp->data[fmtoneptr+0x08+t[i]]&0x1f, - fmp->data[fmtoneptr+0x0c+t[i]]&0x1f, - fmp->data[fmtoneptr+0x10+t[i]]&0x1f, - fmp->data[fmtoneptr+0x14+t[i]]&0x0f, - fmp->data[fmtoneptr+0x14+t[i]]>>4, - fmp->data[fmtoneptr+0x04+t[i]]&0x7f, - fmp->data[fmtoneptr+0x08+t[i]]>>6, - fmp->data[fmtoneptr+0x00+t[i]]&0x0f, - (fmp->data[fmtoneptr+0x00+t[i]]>>4)&0x7 - ); - } fmp_set_tempo(work, fmp); work->driver = fmp; work->driver_opna_interrupt = fmp_opna_interrupt; - fmp_title(work, data, datalen, read16le(data)+4); -// uint16_t pcmptr = read16le(data+read16le(data)-2); - uint16_t pcmptr = read16le(data)-0x12; - if (pcmptr <= datalen && (pcmptr+16) < datalen) { - for (int i = 0; i < 8; i++) { - if (pviname_valid) { - fmp->pvi_name[i] = data[pcmptr+8+i]; - } - if (pviname_valid && fmp->datainfo.flags.ppz) { - fmp->ppz_name[i] = data[pcmptr+0+i]; - } - } - } - return true; } // 4235 diff --git a/fmdriver/fmdriver_fmp.h b/fmdriver/fmdriver_fmp.h index 8ed9451..909387c 100644 --- a/fmdriver/fmdriver_fmp.h +++ b/fmdriver/fmdriver_fmp.h @@ -1,6 +1,10 @@ #ifndef MYON_FMDRIVER_FMP_H_INCLUDED #define MYON_FMDRIVER_FMP_H_INCLUDED +#ifdef __cplusplus +extern "C" { +#endif + #include "fmdriver.h" #include @@ -349,7 +353,8 @@ struct fmp_part { uint16_t deltat; } adpcm; } u; - + + uint8_t tonelen; struct { uint32_t loopstart32; uint32_t loopend32; @@ -516,9 +521,15 @@ struct driver_fmp { } pdzf; }; -// warning: will overwrite data -bool fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp, - uint8_t *data, uint16_t datalen); +// first: call fmp_load with zero_initialized struct driver_fmp and data +// returns true if valid data +// warning: will overwrite data during playback +bool fmp_load(struct driver_fmp *fmp, uint8_t *data, uint16_t datalen); +// then call fmp_init +// this will set the fmp pointer to fmdriver_work::driver +// this function will access opna +void fmp_init(struct fmdriver_work *work, struct driver_fmp *fmp); +// load adpcm data // this function will access opna bool fmp_adpcm_load(struct fmdriver_work *work, uint8_t *data, size_t datalen); @@ -526,4 +537,8 @@ bool fmp_adpcm_load(struct fmdriver_work *work, // 1da8 // 6190: fmp external characters +#ifdef __cplusplus +} +#endif + #endif // MYON_FMDRIVER_FMP_H_INCLUDED diff --git a/fmdsp/fmdsp.c b/fmdsp/fmdsp.c new file mode 100644 index 0000000..4698842 --- /dev/null +++ b/fmdsp/fmdsp.c @@ -0,0 +1,252 @@ +#include "fmdsp.h" +#include "fmdsp_sprites.h" +#include "fmdriver/fmdriver.h" + +static void vramblit(uint8_t *vram, int x, int y, + const uint8_t *data, int w, int h) { + for (int yi = 0; yi < h; yi++) { + for (int xi = 0; xi < w; xi++) { + vram[(y+yi)*PC98_W+(x+xi)] = data[yi*w+xi]; + } + } +} + +static void vramblit_color(uint8_t *vram, int x, int y, + const uint8_t *data, int w, int h, + uint8_t color) { + for (int yi = 0; yi < h; yi++) { + for (int xi = 0; xi < w; xi++) { + vram[(y+yi)*PC98_W+(x+xi)] = data[yi*w+xi] ? color : 0; + } + } +} + +static void vramblit_key(uint8_t *vram, int x, int y, + const uint8_t *data, int w, int h, + uint8_t key, uint8_t color) { + for (int yi = 0; yi < h; yi++) { + for (int xi = 0; xi < w; xi++) { + uint8_t d = data[yi*w+xi]; + if (d == (key+1)) { + vram[(y+yi)*PC98_W+(x+xi)] = color; + } + } + } +} + +void fmdsp_init(struct fmdsp *fmdsp) { + for (int i = 0; i < FMDSP_PALETTE_COLORS; i++) { + fmdsp->palette[i*3+0] = s_palettes[0][i*3+0]; + fmdsp->palette[i*3+1] = s_palettes[0][i*3+1]; + fmdsp->palette[i*3+2] = s_palettes[0][i*3+2]; + } +} + + +static bool sjis_is_mb_start(uint8_t c) { + if (0x81 <= c && c <= 0x9f) return true; + if (0xe0 <= c && c <= 0xef) return true; + return false; +} + +static uint16_t sjis2jis(uint8_t sjis_1st, uint8_t sjis_2nd) { + uint16_t jis; + if (sjis_1st >= 0xe0) sjis_1st -= 0x40; + sjis_1st -= 0x81; + jis = sjis_1st << 9; + if (sjis_2nd >= 0x80) sjis_2nd--; + if (sjis_2nd >= 0x9e) { + jis |= 0x100 | (sjis_2nd - 0x9e); + } else { + jis |= (sjis_2nd - 0x40); + } + jis += 0x2121; + return jis; +} + +static void vram_putchar(uint16_t ptr, uint8_t *vram, const uint8_t *font, + int x, int y, uint8_t color) { + for (int yi = 0; yi < 16; yi++) { + for (int xi = 0; xi < 8; xi++) { + if (font[(ptr<<4)+yi] & (1<<(7-xi))) { + vram[(y+yi)*PC98_W+(x+xi)] = color; + } + } + } +} + +static void fmdsp_putline(const char *strptr, uint8_t *vram, const uint8_t *font, + int y, uint8_t color) { + const uint8_t *cp932str = (const uint8_t *)strptr; + bool sjis_is2nd = false; + uint8_t sjis_1st; + int x = 0; + + while (*cp932str) { + if (!sjis_is2nd) { + if (!sjis_is_mb_start(*cp932str)) { + if (*cp932str == '\t') { + if ((x+8*8) > PC98_W) return; + x += 8*8; + x &= ~(8*8-1); + cp932str++; + } else { + if ((x+8) > PC98_W) return; + vram_putchar(0x8000+*cp932str++, vram, font, x, y, color); + x += 8; + } + } else { + sjis_is2nd = true; + sjis_1st = *cp932str++; + } + } else { + uint8_t sjis_2nd = *cp932str++; + uint16_t jis = sjis2jis(sjis_1st, sjis_2nd); + uint8_t jis_1st = jis >> 8; + uint8_t jis_2nd = jis; + bool half = (jis_1st == 0x29); + if ((x+(half ? 8 : 16)) > PC98_W) return; + vram_putchar((jis_2nd<<8) | (jis_1st-0x20), vram, font, x, y, color); + x += 8; + if (!half) { + vram_putchar((jis_2nd<<8) | (jis_1st-0x20+0x80), vram, font, x, y, color); + x += 8; + } + sjis_is2nd = false; + } + } +} + +void fmdsp_vram_init(struct fmdsp *fmdsp, + struct fmdriver_work *work, + const uint8_t *font, + uint8_t *vram) { + for (int y = 0; y < PC98_H; y++) { + for (int x = 0; x < PC98_W; x++) { + vram[y*PC98_W+x] = 0; + } + } + for (int t = 0; t < 10; t++) { + vramblit(vram, 1, TRACK_H*t+7, s_track, TNAME_W, TNAME_H); + vramblit(vram, KEY_LEFT_X, TRACK_H*t+KEY_Y, s_key_left, KEY_LEFT_W, KEY_H); + for (int i = 0; i < KEY_OCTAVES; i++) { + vramblit(vram, KEY_X+KEY_W*i, TRACK_H*t+KEY_Y, + s_key_bg, KEY_W, KEY_H); + } + vramblit(vram, KEY_X+KEY_W*KEY_OCTAVES, TRACK_H*t+KEY_Y, + s_key_right, KEY_RIGHT_W, KEY_H); + vramblit_color(vram, BAR_L_X, TRACK_H*t+BAR_Y, + s_bar_l, BAR_L_W, BAR_H, 3); + for (int i = 0; i < BAR_CNT; i++) { + vramblit_color(vram, BAR_X+BAR_W*i, TRACK_H*t+BAR_Y, + s_bar, BAR_W, BAR_H, 3); + } + } + vramblit(vram, PLAYING_X, PLAYING_Y, + s_playing, PLAYING_W, PLAYING_H); + for (int x = 74; x < PC98_W; x++) { + vram[332*PC98_W+x] = 7; + } + int height = (16+3)*3+8; + for (int y = PC98_H-height; y < PC98_H; y++) { + for (int x = 0; x < PC98_W; x++) { + vram[y*PC98_W+x] = (y&1)^(x&1) ? 3 : 0; + } + } + vram[(PC98_H-height)*PC98_W] = 0; + vram[(PC98_H-1)*PC98_W] = 0; + for (int i = 0; i < 3; i++) { + fmdsp_putline(work->comment[i], vram, font, COMMENT_Y+COMMENT_H*i, 2); + } +} + +void fmdsp_update(struct fmdsp *fmdsp, + const struct fmdriver_work *work, uint8_t *vram) { + for (int t = 0; t < 10; t++) { + struct fmdriver_track_status *track = &work->track_status[t]; + uint8_t *track_type; + switch (track->type) { + case FMDRIVER_TRACK_FM: + track_type = s_t_fm; + break; + case FMDRIVER_TRACK_SSG: + track_type = s_t_ssg; + break; + case FMDRIVER_TRACK_ADPCM: + track_type = s_t_adpcm; + break; + case FMDRIVER_TRACK_PPZ8: + track_type = s_t_ppz8; + break; + } + vramblit(vram, 1, TRACK_H*t+1, track_type, TNAME_W, TNAME_H); + vramblit(vram, NUM_X+NUM_W*0, TRACK_H*t+1, s_num[(track->num/10)%10], NUM_W, NUM_H); + vramblit(vram, NUM_X+NUM_W*1, TRACK_H*t+1, s_num[track->num%10], NUM_W, NUM_H); + for (int i = 0; i < KEY_OCTAVES; i++) { + vramblit(vram, KEY_X+KEY_W*i, TRACK_H*t+KEY_Y, + s_key_bg, KEY_W, KEY_H); + if (track->playing) { + if (track->actual_key >> 4 == i) { + vramblit_key(vram, KEY_X+KEY_W*i, TRACK_H*t+KEY_Y, + s_key_mask, KEY_W, KEY_H, + track->actual_key & 0xf, 8); + } + if (track->key >> 4 == i) { + vramblit_key(vram, KEY_X+KEY_W*i, TRACK_H*t+KEY_Y, + s_key_mask, KEY_W, KEY_H, + track->key & 0xf, 6); + } + } + } + uint8_t color_on = track->key == 0xff ? 7 : 2; + if (!track->playing) color_on = 3; + vramblit_color(vram, BAR_L_X, TRACK_H*t+BAR_Y, + s_bar_l, BAR_L_W, BAR_H, color_on); + for (int i = 0; i < BAR_CNT; i++) { + int c = (i < (track->ticks_left>>2)) ? color_on : 3; + vramblit_color(vram, BAR_X+BAR_W*i, TRACK_H*t+BAR_Y, + s_bar, BAR_W, BAR_H, c); + } + vramblit_color(vram, BAR_X+BAR_W*(track->ticks>>2), TRACK_H*t+BAR_Y, + s_bar, BAR_W, BAR_H, 7); + } +} + +void fmdsp_vrampalette(struct fmdsp *fmdsp, const uint8_t *vram, uint8_t *vram32, int stride) { + for (int y = 0; y < PC98_H; y++) { + for (int x = 0; x < PC98_W; x++) { + uint8_t r = fmdsp->palette[vram[y*PC98_W+x]*3+0]; + uint8_t g = fmdsp->palette[vram[y*PC98_W+x]*3+1]; + uint8_t b = fmdsp->palette[vram[y*PC98_W+x]*3+2]; + uint32_t data = (((uint32_t)r)<<16) | (((uint32_t)g)<<8) | ((uint32_t)b); + uint32_t *row = (uint32_t *)(vram32 + y*stride); + row[x] = data; + } + } +} + +//2/1 - 7/14 +// 0x21 - 0x7e +static void fontrom_copy_rows(uint8_t *font, const uint8_t *fontrom, + int rowstart, int rowend) { + for (int row = rowstart; row < rowend; row++) { + for (int cell = 0x20; cell < 0x80; cell++) { + for (int y = 0; y < 16; y++) { + // left + font[0x000+((row-0x20)<<4)+(cell<<12)+y] = fontrom[0x800+(0x60*16*2*(row-0x20))+(cell<<5)+y]; + // right + font[0x800+((row-0x20)<<4)+(cell<<12)+y] = fontrom[0x800+(0x60*16*2*(row-0x20))+(cell<<5)+y+16]; + } + } + } +} + +void fmdsp_font_from_fontrom(uint8_t *font, const uint8_t *fontrom) { + // ANK + for (int i = 0; i < 256*16; i++) { + font[0x80000+i] = fontrom[0x800+i]; + } + fontrom_copy_rows(font, fontrom, 0x21, 0x50); + fontrom_copy_rows(font, fontrom, 0x50, 0x76); + fontrom_copy_rows(font, fontrom, 0x78, 0x7d); +} diff --git a/fmdsp/fmdsp.h b/fmdsp/fmdsp.h new file mode 100644 index 0000000..4c6af0c --- /dev/null +++ b/fmdsp/fmdsp.h @@ -0,0 +1,37 @@ +#ifndef MYON_FMDSP_H_INCLUDED +#define MYON_FMDSP_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + PC98_W = 640, + PC98_H = 400 +}; + +enum { + FMDSP_PALETTE_COLORS = 9 +}; + +struct fmdsp { + uint8_t palette[FMDSP_PALETTE_COLORS*3]; + uint8_t target_palette[FMDSP_PALETTE_COLORS*3]; +}; + +struct fmdriver_work; +void fmdsp_init(struct fmdsp *fmdsp); +void fmdsp_vram_init(struct fmdsp *fmdsp, + struct fmdriver_work *work, + const uint8_t *font, + uint8_t *vram); +void fmdsp_update(struct fmdsp *fmdsp, const struct fmdriver_work *work, uint8_t *vram); +void fmdsp_vrampalette(struct fmdsp *fmdsp, const uint8_t *vram, uint8_t *vram32, int stride); +void fmdsp_font_from_fontrom(uint8_t *font, const uint8_t *fontrom); +#ifdef __cplusplus +} +#endif + +#endif // MYON_FMDSP_H_INCLUDED diff --git a/fmdsp/fmdsp_sprites.h b/fmdsp/fmdsp_sprites.h new file mode 100644 index 0000000..8554985 --- /dev/null +++ b/fmdsp/fmdsp_sprites.h @@ -0,0 +1,318 @@ +static const uint8_t test[] = { + 0, 2, 2, 2, + 2, 0, 0, 0, + 2, 0, 2, 2, + 2, 0, 0, 0, + 2, 0, 0, 0, +}; + +enum { + TRACK_H = 32, + TNAME_W = 26, + TNAME_H = 5, + NUM_X = 31, + NUM_W = 8, + NUM_H = 11, + KEY_X = 8, + KEY_Y = 14, + KEY_W = 35, + KEY_H = 17, + KEY_LEFT_X = 1, + KEY_LEFT_W = 6, + KEY_RIGHT_W = 11, + KEY_OCTAVES = 8, + BAR_L_X = 68, + BAR_L_W = 14, + BAR_X = BAR_L_X + BAR_L_W, + BAR_Y = 1, + BAR_W = 2, + BAR_H = 4, + BAR_CNT = 64, + COMMENT_Y = 340, + COMMENT_H = 19, + PLAYING_X = 0, + PLAYING_Y = 324, + PLAYING_W = 72, + PLAYING_H = 9, +}; + +static const uint8_t s_palettes[1][FMDSP_PALETTE_COLORS*3] = { + { + 0, 0, 0, + 170, 170, 153, + 102, 136, 255, + 68, 68, 119, + 204, 204, 187, + 102, 102, 85, + 136, 255, 68, + 51, 51, 238, + 0, 187, 255, + } +}; + +static const uint8_t s_track[TNAME_W*TNAME_H] = { + 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, +}; +static const uint8_t s_t_fm[TNAME_W*TNAME_H] = { + 0, 2, 2, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const uint8_t s_t_ssg[TNAME_W*TNAME_H] = { + 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 2, 2, 0, 0, 2, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const uint8_t s_t_adpcm[TNAME_W*TNAME_H] = { + 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 2, 0, 0, 2, 0, 0, +}; +static const uint8_t s_t_ppz8[TNAME_W*TNAME_H] = { + 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const uint8_t s_num[10][NUM_W*NUM_H] = { + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 0, 3, 3, 0, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 3, 3, 3, 0, 0, + 0, 0, 3, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 0, 3, 3, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 3, 3, 3, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 3, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 2, 0, 0, 0, 3, 0, 0, + 0, 2, 0, 0, 0, 3, 0, 0, + 0, 2, 0, 0, 0, 3, 0, 0, + 0, 2, 0, 0, 0, 3, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 3, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 3, 3, 3, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 3, 3, 3, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 0, 3, 3, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 3, 3, 3, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + }, + { + 0, 0, 0, 2, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, + } +}; +static const uint8_t s_key_bg[KEY_W*KEY_H] = { + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,5,0,0,4,4,5,0,0,4,4,4,0,4,4,4,5,0,0,4,4,5,0,0,4,4,5,0,0,4,4,4,0, + 4,4,4,5,5,0,4,4,5,5,0,4,4,4,0,4,4,4,5,5,0,4,4,5,5,0,4,4,5,5,0,4,4,4,0, + 4,4,4,0,0,0,4,4,0,0,0,4,4,4,0,4,4,4,0,0,0,4,4,0,0,0,4,4,0,0,0,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0,4,4,4,4,0, + 5,4,4,5,0,5,4,4,5,0,5,4,4,5,0,5,4,4,5,0,5,4,4,5,0,5,4,4,5,0,5,4,4,5,0, +}; +static const uint8_t s_key_left[KEY_LEFT_W*KEY_H] = { + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 5, 0, 0, 4, 4, 4, + 5, 5, 0, 4, 4, 4, + 0, 0, 0, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 4, 0, 4, 4, 4, 4, + 5, 0, 5, 4, 4, 5, +}; +static const uint8_t s_key_right[KEY_RIGHT_W*KEY_H] = { + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 5, 0, 0, 4, 4, 5, 0, 0, + 4, 4, 4, 5, 5, 0, 4, 4, 5, 5, 0, + 4, 4, 4, 0, 0, 0, 4, 4, 0, 0, 0, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 4, + 5, 4, 4, 5, 0, 5, 4, 4, 5, 0, 5, +}; +static const uint8_t s_key_mask[KEY_W*KEY_H] = { + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,0,2,2,3,3,0,4,4,5,5,5,0,6,6,6,0,7,7,8,8,0,9,9, 10,10,0, 11,11,12,12,12,0, + 1,1,1,0,0,2,3,3,0,0,4,5,5,5,0,6,6,6,0,0,7,8,8,0,0,9, 10,10,0, 0, 11,12,12,12,0, + 1,1,1,2,2,2,3,3,4,4,4,5,5,5,0,6,6,6,7,7,7,8,8,9,9,9, 10,10,11,11,11,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 1,1,1,1,0,3,3,3,3,0,5,5,5,5,0,6,6,6,6,0,8,8,8,8,0,10,10,10,10,0, 12,12,12,12,0, + 0,1,1,0,0,0,3,3,0,0,0,5,5,0,0,0,6,6,0,0,0,8,8,0,0,0, 10,10,0, 0, 0, 12,12,0, 0, +}; +static const uint8_t s_bar_l[BAR_L_W*BAR_H] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, +}; +static const uint8_t s_bar[BAR_W*BAR_H] = { + 1, 0, + 1, 0, + 1, 0, + 1, 0, +}; +static const uint8_t s_playing[PLAYING_W*PLAYING_H] = { + 2,2,2,2,2,2,2,0,0,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,2,2,0,0,0,0,2,2,0,2,2,0,2,2,0,0,0,0,2,2,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,2,2,0,0,2,2,0,0,2,2,0,0,2,2,0,2,2,2,0,0,0,2,2,0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,2,2,2,2,0,0,0,2,2,0,2,2,2,2,0,0,2,2,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,2,2,0,2,2,0,2,2,0,2,2,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,0,0,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,2,2,0,0,2,2,2,2,0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,2,2,0,0,0,2,2,2,0,2,2,0,0,0,0,2,2,0,0,2,2,0,0,2,2,0,0,2,2,0,0,2,2,0, + 2,2,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,0,0,0,2,2,0,2,2,0,0,0,0,2,2,0,0,2,2,2,2,2,2,0,0,0,2,2,0,0,2,2,0,0,2,2,0,0,2,2,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +}; diff --git a/gtk/.gitignore b/gtk/.gitignore new file mode 100644 index 0000000..ea5e662 --- /dev/null +++ b/gtk/.gitignore @@ -0,0 +1 @@ +fmplayer diff --git a/gtk/Makefile.am b/gtk/Makefile.am new file mode 100644 index 0000000..9a4790b --- /dev/null +++ b/gtk/Makefile.am @@ -0,0 +1,23 @@ +bin_PROGRAMS=fmplayer + +LIBOPNA_SRC=../libopna/opnaadpcm.c \ + ../libopna/opnadrum.c \ + ../libopna/opnafm.c \ + ../libopna/opnassg.c \ + ../libopna/opnatimer.c \ + ../libopna/opna.c + +FMDRIVER_SRC=../fmdriver/fmdriver_fmp.c \ + ../fmdriver/ppz8.c + +FMDSP_SRC=../fmdsp/fmdsp.c + +fmplayer_SOURCES=main.c \ + $(LIBOPNA_SRC) \ + $(FMDRIVER_SRC) \ + $(FMDSP_SRC) + +fmplayer_CPPFLAGS=-Wall -Wextra -pedantic \ + -I.. \ + $(GTK3_CFLAGS) $(PORTAUDIO_CFLAGS) +fmplayer_LDADD=$(GTK3_LIBS) $(PORTAUDIO_LIBS) diff --git a/gtk/configure.ac b/gtk/configure.ac new file mode 100644 index 0000000..bde5f6e --- /dev/null +++ b/gtk/configure.ac @@ -0,0 +1,10 @@ +AC_INIT([fmplayer], [0.1.0]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) +AC_PROG_CC_C99 + +dnl AM_PATH_SDL2([2.0.5]) +PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0]) +PKG_CHECK_MODULES([GTK3], [gtk+-3.0 cairo]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/gtk/main.c b/gtk/main.c new file mode 100644 index 0000000..7dd183a --- /dev/null +++ b/gtk/main.c @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fmdriver/fmdriver_fmp.h" +#include "fmdriver/ppz8.h" +#include "libopna/opna.h" +#include "libopna/opnatimer.h" +#include "fmdsp/fmdsp.h" + +#define DATADIR "/.local/share/fmplayer/" + +enum { + SRATE = 55467, + PPZ8MIX = 0xa000, + FONT_ROM_SIZE = 0x84000, + FONT_ROM_FILESIZE = 0x46800, + AUDIOBUFLEN = 0, +}; + +static struct { + GtkWidget *mainwin; + bool pa_initialized; + PaStream *pastream; + struct opna opna; + struct opna_timer opna_timer; + struct ppz8 ppz8; + struct fmdriver_work work; + struct fmdsp fmdsp; + char drum_rom[OPNA_ROM_SIZE]; + bool drum_rom_loaded; + char adpcm_ram[OPNA_ADPCM_RAM_SIZE]; + struct driver_fmp *fmp; + void *fmpdata; + void *ppzbuf; + uint8_t vram[PC98_W*PC98_H]; + uint8_t font[FONT_ROM_SIZE]; + void *vram32; + int vram32_stride; +} g; + +static void quit(void) { + if (g.pastream) { + Pa_CloseStream(g.pastream); + } + if (g.pa_initialized) Pa_Terminate(); + free(g.fmp); + free(g.fmpdata); + free(g.ppzbuf); + gtk_main_quit(); +} + +static void on_destroy(GtkWidget *w, gpointer ptr) { + quit(); +} + +static void on_menu_quit(GtkMenuItem *menuitem, gpointer ptr) { + quit(); +} + +static void msgbox_err(const char *msg) { + GtkWidget *d = gtk_message_dialog_new(GTK_WINDOW(g.mainwin), GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + msg); + gtk_dialog_run(GTK_DIALOG(d)); + gtk_widget_destroy(d); +} + + +static int pastream_cb(const void *inptr, void *outptr, unsigned long frames, + const PaStreamCallbackTimeInfo *timeinfo, + PaStreamCallbackFlags statusFlags, + void *userdata) { + struct opna_timer *timer = (struct opna_timer *)userdata; + int16_t *buf = (int16_t *)outptr; + memset(outptr, 0, sizeof(int16_t)*frames*2); + opna_timer_mix(timer, buf, frames); + return paContinue; +} + +static void opna_int_cb(void *userptr) { + struct fmdriver_work *work = (struct fmdriver_work *)userptr; + work->driver_opna_interrupt(work); +} + +static void opna_mix_cb(void *userptr, int16_t *buf, unsigned samples) { + struct ppz8 *ppz8 = (struct ppz8 *)userptr; + ppz8_mix(ppz8, buf, samples); +} + +static void opna_writereg_libopna(struct fmdriver_work *work, unsigned addr, unsigned data) { + struct opna_timer *timer = (struct opna_timer *)work->opna; + opna_timer_writereg(timer, addr, data); +} + +static uint8_t opna_status_libopna(struct fmdriver_work *work, bool a1) { + struct opna_timer *timer = (struct opna_timer *)work->opna; + uint8_t status = opna_timer_status(timer); + if (!a1) { + status &= 0x83; + } + return status; +} + +static FILE *pvisearch(const char *filename, const char *pvibase) { + // TODO: not SJIS aware + char pviname[8+3+2] = {0}; + char pviname_l[8+3+2] = {0}; + strcpy(pviname, pvibase); + strcat(pviname, ".PVI"); + strcpy(pviname_l, pviname); + for (char *c = pviname_l; *c; c++) { + if (('A' <= *c) && (*c <= 'Z')) { + *c += ('a' - 'A'); + } + } + FILE *pvifile = fopen(pviname, "r"); + if (pvifile) return pvifile; + pvifile = fopen(pviname_l, "r"); + if (pvifile) return pvifile; + char *slash = strrchr(filename, '/'); + if (!slash) return 0; + char *pvipath = malloc((slash-filename)+1+sizeof(pviname)); + if (!pvipath) return 0; + memcpy(pvipath, filename, slash-filename+1); + pvipath[slash-filename+1] = 0; + strcat(pvipath, pviname); + pvifile = fopen(pvipath, "r"); + if (pvifile) { + free(pvipath); + return pvifile; + } + pvipath[slash-filename+1] = 0; + strcat(pvipath, pviname_l); + pvifile = fopen(pvipath, "r"); + if (pvifile) { + free(pvipath); + return pvifile; + } + free(pvipath); + return 0; +} + +static bool loadpvi(struct fmdriver_work *work, + struct driver_fmp *fmp, + const char *filename) { + // no need to load, always success + if(strlen(fmp->pvi_name) == 0) return true; + FILE *pvifile = pvisearch(filename, fmp->pvi_name); + if (!pvifile) goto err; + if (fseek(pvifile, 0, SEEK_END) != 0) goto err_file; + size_t fsize; + { + long size = ftell(pvifile); + if (size < 0) goto err_file; + fsize = size; + } + if (fseek(pvifile, 0, SEEK_SET) != 0) goto err_file; + void *data = malloc(fsize); + if (!data) goto err_file; + if (fread(data, 1, fsize, pvifile) != fsize) goto err_memory; + if (!fmp_adpcm_load(work, data, fsize)) goto err_memory; + free(data); + fclose(pvifile); + return true; +err_memory: + free(data); +err_file: + fclose(pvifile); +err: + return false; +} + +static bool loadppzpvi(struct fmdriver_work *work, + struct driver_fmp *fmp, + const char *filename) { + // no need to load, always success + if(strlen(fmp->ppz_name) == 0) return true; + FILE *pvifile = pvisearch(filename, fmp->ppz_name); + if (!pvifile) goto err; + if (fseek(pvifile, 0, SEEK_END) != 0) goto err_file; + size_t fsize; + { + long size = ftell(pvifile); + if (size < 0) goto err_file; + fsize = size; + } + if (fseek(pvifile, 0, SEEK_SET) != 0) goto err_file; + void *data = malloc(fsize); + if (!data) goto err_file; + if (fread(data, 1, fsize, pvifile) != fsize) goto err_memory; + int16_t *decbuf = calloc(ppz8_pvi_decodebuf_samples(fsize), sizeof(int16_t)); + if (!decbuf) goto err_memory; + if (!ppz8_pvi_load(work->ppz8, 0, data, fsize, decbuf)) goto err_decbuf; + free(g.ppzbuf); + g.ppzbuf = decbuf; + free(data); + fclose(pvifile); + return true; +err_decbuf: + free(decbuf); +err_memory: + free(data); +err_file: + fclose(pvifile); +err: + return false; +} + +static void load_drumrom(void) { + const char *path = "ym2608_adpcm_rom.bin"; + const char *home = getenv("HOME"); + char *dpath = 0; + if (home) { + const char *datadir = DATADIR; + dpath = malloc(strlen(home)+strlen(datadir)+strlen(path) + 1); + if (dpath) { + strcpy(dpath, home); + strcat(dpath, datadir); + strcat(dpath, path); + path = dpath; + } + } + FILE *rhythm = fopen(path, "r"); + free(dpath); + if (!rhythm) goto err; + if (fseek(rhythm, 0, SEEK_END) != 0) goto err_file; + long size = ftell(rhythm); + if (size != OPNA_ROM_SIZE) goto err_file; + if (fseek(rhythm, 0, SEEK_SET) != 0) goto err_file; + if (fread(g.drum_rom, 1, OPNA_ROM_SIZE, rhythm) != OPNA_ROM_SIZE) goto err_file; + fclose(rhythm); + g.drum_rom_loaded = true; + return; +err_file: + fclose(rhythm); +err: + return; +} + +static void load_fontrom(void) { + const char *path = "font.rom"; + const char *home = getenv("HOME"); + char *dpath = 0; + if (home) { + const char *datadir = DATADIR; + dpath = malloc(strlen(home)+strlen(datadir)+strlen(path) + 1); + if (dpath) { + strcpy(dpath, home); + strcat(dpath, datadir); + strcat(dpath, path); + path = dpath; + } + } + FILE *font = fopen(path, "r"); + free(dpath); + if (!font) goto err; + if (fseek(font, 0, SEEK_END) != 0) goto err_file; + long size = ftell(font); + if (size != FONT_ROM_FILESIZE) goto err_file; + if (fseek(font, 0, SEEK_SET) != 0) goto err_file; + uint8_t *fontbuf = malloc(FONT_ROM_FILESIZE); + if (!fontbuf) goto err_file; + if (fread(fontbuf, 1, FONT_ROM_FILESIZE, font) != FONT_ROM_FILESIZE) goto err_fontbuf; + fmdsp_font_from_fontrom(g.font, fontbuf); + free(fontbuf); + fclose(font); + return; +err_fontbuf: + free(fontbuf); +err_file: + fclose(font); +err: + return; +} + +static bool openfile(const char *path) { + if (!g.pa_initialized) { + msgbox_err("Could not initialize Portaudio"); + goto err; + } + FILE *fmfile = fopen(path, "r"); + if (!fmfile) { + msgbox_err("Cannot open file"); + goto err; + } + if (fseek(fmfile, 0, SEEK_END) != 0) { + msgbox_err("cannot seek file to end"); + goto err_file; + } + size_t filelen; + { + long tfilelen = ftell(fmfile); + if ((tfilelen < 0) || (tfilelen > 0xffff)) { + msgbox_err("invalid file length"); + goto err_file; + } + filelen = tfilelen; + } + if (fseek(fmfile, 0, SEEK_SET) != 0) { + msgbox_err("cannot seek file to beginning"); + goto err_file; + } + void *fmbuf = malloc(filelen); + if (!fmbuf) { + msgbox_err("cannot allocate memory for file"); + goto err_file; + } + if (fread(fmbuf, 1, filelen, fmfile) != filelen) { + msgbox_err("cannot read file"); + goto err_fmbuf; + } + struct driver_fmp *fmp = calloc(1, sizeof(struct driver_fmp)); + if (!fmp) { + msgbox_err("cannot allocate memory for fmp"); + goto err_fmbuf; + } + if (!fmp_load(fmp, fmbuf, filelen)) { + msgbox_err("invalid FMP file"); + goto err_fmp; + } + if (!g.pastream) { + PaError pe = Pa_OpenDefaultStream(&g.pastream, 0, 2, paInt16, SRATE, AUDIOBUFLEN, + pastream_cb, &g.opna_timer); + if (pe != paNoError) { + msgbox_err("cannot open portaudio stream"); + goto err_fmp; + } + } else { + PaError pe = Pa_StopStream(g.pastream); + if (pe != paNoError) { + msgbox_err("Portaudio Error"); + goto err_fmp; + } + } + free(g.fmp); + g.fmp = fmp; + free(g.fmpdata); + g.fmpdata = fmbuf; + opna_reset(&g.opna); + if (!g.drum_rom_loaded) { + load_drumrom(); + } + if (g.drum_rom_loaded) { + opna_drum_set_rom(&g.opna.drum, g.drum_rom); + } + opna_adpcm_set_ram_256k(&g.opna.adpcm, g.adpcm_ram); + ppz8_init(&g.ppz8, SRATE, PPZ8MIX); + opna_timer_reset(&g.opna_timer, &g.opna); + memset(&g.work, 0, sizeof(g.work)); + g.work.opna_writereg = opna_writereg_libopna; + g.work.opna_status = opna_status_libopna; + g.work.opna = &g.opna_timer; + g.work.ppz8 = &g.ppz8; + g.work.ppz8_functbl = &ppz8_functbl; + opna_timer_set_int_callback(&g.opna_timer, opna_int_cb, &g.work); + opna_timer_set_mix_callback(&g.opna_timer, opna_mix_cb, &g.ppz8); + fmp_init(&g.work, g.fmp); + fmdsp_vram_init(&g.fmdsp, &g.work, g.font, g.vram); + loadpvi(&g.work, g.fmp, path); + loadppzpvi(&g.work, g.fmp, path); + fclose(fmfile); + Pa_StartStream(g.pastream); + return true; +err_fmp: + free(fmp); +err_fmbuf: + free(fmbuf); +err_file: + fclose(fmfile); +err: + return false; +} + +static void on_file_activated(GtkFileChooser *chooser, gpointer ptr) { + gchar *filename = gtk_file_chooser_get_filename(chooser); + if (filename) { + openfile(filename); + g_free(filename); + } +} + +static GtkWidget *create_menubar() { + GtkWidget *menubar = gtk_menu_bar_new(); + GtkWidget *menu = gtk_menu_new(); + GtkWidget *file = gtk_menu_item_new_with_label("File"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), menu); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file); + GtkWidget *open = gtk_menu_item_new_with_label("Open"); + //g_signal_connect(open, "activate", G_CALLBACK(on_menu_open), 0); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), open); + GtkWidget *quit = gtk_menu_item_new_with_label("Quit"); + g_signal_connect(quit, "activate", G_CALLBACK(on_menu_quit), 0); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit); + return menubar; +} + +static gboolean draw_cb(GtkWidget *w, + cairo_t *cr, + gpointer p) { + fmdsp_update(&g.fmdsp, &g.work, g.vram); + fmdsp_vrampalette(&g.fmdsp, g.vram, g.vram32, g.vram32_stride); + cairo_surface_t *s = cairo_image_surface_create_for_data( + g.vram32, CAIRO_FORMAT_RGB24, PC98_W, PC98_H, g.vram32_stride); + cairo_scale(cr, 2.0, 2.0); + cairo_set_source_surface(cr, s, 0.0, 0.0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + cairo_paint(cr); + cairo_surface_destroy(s); + return FALSE; +} + +static gboolean tick_cb(GtkWidget *w, + GdkFrameClock *frame_clock, + gpointer p) { + (void)frame_clock; + gtk_widget_queue_draw(GTK_WIDGET(p)); + return G_SOURCE_CONTINUE; +} + +static void destroynothing(gpointer p) { + (void)p; +} + +int main(int argc, char **argv) { + load_fontrom(); + gtk_init(&argc, &argv); + GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g.mainwin = w; + //gtk_window_set_resizable(GTK_WINDOW(w), FALSE); + gtk_window_set_title(GTK_WINDOW(w), "FMPlayer"); + g_signal_connect(w, "destroy", G_CALLBACK(on_destroy), 0); + GtkWidget *box = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(w), box); + + GtkWidget *menubar = create_menubar(); + gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, TRUE, 0); + + GtkWidget *hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + GtkWidget *drawarea = gtk_drawing_area_new(); + gtk_widget_set_size_request(drawarea, PC98_W*2, PC98_H*2); + g_signal_connect(drawarea, "draw", G_CALLBACK(draw_cb), 0); + gtk_box_pack_start(GTK_BOX(hbox), drawarea, FALSE, TRUE, 0); + + GtkWidget *filechooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN); + g_signal_connect(filechooser, "file-activated", G_CALLBACK(on_file_activated), 0); + gtk_box_pack_start(GTK_BOX(hbox), filechooser, TRUE, TRUE, 0); + + + g.pa_initialized = (Pa_Initialize() == paNoError); + fmdsp_init(&g.fmdsp); + fmdsp_vram_init(&g.fmdsp, &g.work, g.font, g.vram); + g.vram32_stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, PC98_W); + g.vram32 = malloc((g.vram32_stride*PC98_H)*4); + + gtk_widget_show_all(w); + gtk_widget_add_tick_callback(w, tick_cb, drawarea, destroynothing); + gtk_main(); + return 0; +} diff --git a/libopna/opnadrum.h b/libopna/opnadrum.h index 24bcd48..7c13098 100644 --- a/libopna/opnadrum.h +++ b/libopna/opnadrum.h @@ -14,7 +14,7 @@ extern "C" { #define OPNA_ROM_HH_START 0x1b80 #define OPNA_ROM_TOM_START 0x1d00 #define OPNA_ROM_RIM_START 0x1f80 -#define OPNA_ROM_SIZE 0x2000 +#define OPNA_ROM_SIZE 0x2000 #define OPNA_ROM_BD_SIZE ((OPNA_ROM_SD_START-OPNA_ROM_BD_START)*2*3) #define OPNA_ROM_SD_SIZE ((OPNA_ROM_TOP_START-OPNA_ROM_SD_START)*2*3) -- cgit v1.2.3