diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2016-11-30 23:01:06 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2016-11-30 23:01:06 +0900 | 
| commit | ae78fca8bf5835ceccdbdc902197fe082b8def30 (patch) | |
| tree | 1784d80a55455e88aad56c794e59d432a5f0f33b | |
| parent | 6fa6b5535a9f514ae527af58a2c0964645d26e6f (diff) | |
added GTK UI
| -rw-r--r-- | .gitignore | 11 | ||||
| -rw-r--r-- | curses/.gitignore | 11 | ||||
| -rw-r--r-- | curses/main.c | 59 | ||||
| -rw-r--r-- | fmdriver/fmdriver.h | 29 | ||||
| -rw-r--r-- | fmdriver/fmdriver_common.h | 5 | ||||
| -rw-r--r-- | fmdriver/fmdriver_fmp.c | 275 | ||||
| -rw-r--r-- | fmdriver/fmdriver_fmp.h | 23 | ||||
| -rw-r--r-- | fmdsp/fmdsp.c | 252 | ||||
| -rw-r--r-- | fmdsp/fmdsp.h | 37 | ||||
| -rw-r--r-- | fmdsp/fmdsp_sprites.h | 318 | ||||
| -rw-r--r-- | gtk/.gitignore | 1 | ||||
| -rw-r--r-- | gtk/Makefile.am | 23 | ||||
| -rw-r--r-- | gtk/configure.ac | 10 | ||||
| -rw-r--r-- | gtk/main.c | 466 | ||||
| -rw-r--r-- | libopna/opnadrum.h | 2 | 
15 files changed, 1432 insertions, 90 deletions
| @@ -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 <stdbool.h>  #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 <stdio.h>  #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 <stddef.h> @@ -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 <stdint.h> + +#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 <gtk/gtk.h> +#include <portaudio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> +#include <cairo.h> + +#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) | 
