aboutsummaryrefslogtreecommitdiff
path: root/fmdriver
diff options
context:
space:
mode:
Diffstat (limited to 'fmdriver')
-rw-r--r--fmdriver/fmdriver.h29
-rw-r--r--fmdriver/fmdriver_common.h5
-rw-r--r--fmdriver/fmdriver_fmp.c275
-rw-r--r--fmdriver/fmdriver_fmp.h23
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