diff options
Diffstat (limited to 'fmdriver')
| -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 | 
4 files changed, 281 insertions, 51 deletions
| 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 | 
