aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakamichi Horikawa <takamichiho@gmail.com>2017-08-13 23:08:54 +0900
committerTakamichi Horikawa <takamichiho@gmail.com>2017-08-13 23:08:54 +0900
commit5a8af1f670709d6cd28ad5edda223d9ecbc03bc3 (patch)
treed8819443b937c49b5a4feb12ad54178d6f79a584
parent11a5698d68461b91a681f37d66fad5e206969674 (diff)
added levelmeter
-rw-r--r--fmdriver/fmdriver_fmp.c59
-rw-r--r--fmdriver/fmdriver_fmp.h1
-rw-r--r--fmdriver/ppz8.c11
-rw-r--r--fmdriver/ppz8.h3
-rw-r--r--fmdsp/fmdsp.c176
-rw-r--r--fmdsp/fmdsp.h11
-rw-r--r--fmdsp/fmdsp_sprites.h119
-rw-r--r--libopna/opnaadpcm.c8
-rw-r--r--libopna/opnaadpcm.h3
-rw-r--r--libopna/opnadrum.c7
-rw-r--r--libopna/opnadrum.h2
-rw-r--r--libopna/opnafm.c13
-rw-r--r--libopna/opnafm.h3
-rw-r--r--libopna/opnassg.c13
-rw-r--r--libopna/opnassg.h2
15 files changed, 398 insertions, 33 deletions
diff --git a/fmdriver/fmdriver_fmp.c b/fmdriver/fmdriver_fmp.c
index 5e01089..74c119c 100644
--- a/fmdriver/fmdriver_fmp.c
+++ b/fmdriver/fmdriver_fmp.c
@@ -2698,6 +2698,7 @@ static void fmp_part_cmd_rhythm(struct fmdriver_work *work,
uint8_t voice = fmp->pdzf.rhythm[p&1].voice[((rhythm->volumes[p] & 0x10) >> 4)];
uint8_t pan = fmp->pdzf.rhythm[p&1].pan + 5;
uint32_t freq = fmp_ppz8_note_freq(fmp->pdzf.rhythm[p&1].note);
+ fmp->pdzf.rhythm_current_note = fmp->pdzf.rhythm[p&1].note;
uint8_t channel = 6;
work->ppz8_functbl->channel_play(
work->ppz8,
@@ -2777,15 +2778,40 @@ static void fmp_work_status_init(struct fmdriver_work *work,
static void fmp_work_status_update(struct fmdriver_work *work,
const struct driver_fmp *fmp) {
work->ssg_noise_freq = fmp->ssg_noise_freq;
- for (int t = 0; t < FMDRIVER_TRACK_NUM; t++) {
+ for (int t = 0; t < FMDRIVER_TRACK_PPZ8_1; t++) {
struct fmdriver_track_status *track = &work->track_status[t];
const struct fmp_part *part = &fmp->parts[fmp_track_map[t]];
track->playing = !part->status.off;
track->info = FMDRIVER_TRACK_INFO_NORMAL;
+ 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);
+ track->tonenum = part->tone;
+ track->detune = part->detune - ((part->detune & 0x8000) ? 0x10000 : 0);
+ track->status[0] = part->lfo_f.p ? 'P' : '-';
+ track->status[1] = part->lfo_f.q ? 'Q' : '-';
+ track->status[2] = part->lfo_f.r ? 'R' : '-';
+ track->status[3] = part->lfo_f.a ? 'A' : '-';
+ track->status[4] = '-';
+ track->status[5] = part->lfo_f.e ? 'e' : '-';
+ track->status[6] = (part->type.fm && part->u.fm.hlfo_apms) ? 'H' : '-';
+ track->status[7] = part->status.pitchbend ? 'P' : '-';
if (part->type.adpcm) {
track->actual_key = 0xff;
track->volume = part->actual_vol;
} else if (part->type.ssg) {
+ struct fmdriver_track_status *ppztrack =
+ &work->track_status[FMDRIVER_TRACK_PPZ8_1-1+track->ppz8_ch];
+ ppztrack->actual_key = 0xff;
+ if (part->pdzf.mode || part->u.ssg.env_f.ppz) {
+ ppztrack->playing = !part->status.off;
+ ppztrack->key = track->key;
+ ppztrack->tonenum = track->tonenum;
+ ppztrack->info = FMDRIVER_TRACK_INFO_NORMAL;
+ } else {
+ ppztrack->playing = false;
+ ppztrack->key = 0xff;
+ }
if (part->pdzf.mode) {
track->info = FMDRIVER_TRACK_INFO_PDZF;
track->actual_key = 0xff;
@@ -2801,8 +2827,15 @@ static void fmp_work_status_update(struct fmdriver_work *work,
track->volume = part->current_vol - 1;
} else {
if (part->pdzf.mode) {
+ struct fmdriver_track_status *ppztrack =
+ &work->track_status[FMDRIVER_TRACK_PPZ8_1-1+track->ppz8_ch];
track->info = FMDRIVER_TRACK_INFO_PDZF;
track->actual_key = 0xff;
+ ppztrack->actual_key = 0xff;
+ ppztrack->playing = !part->status.off;
+ ppztrack->key = track->key;
+ ppztrack->tonenum = work->ppz8 ? work->ppz8->channel[track->ppz8_ch-1].voice : 0;
+ ppztrack->info = FMDRIVER_TRACK_INFO_NORMAL;
} else {
if (part->u.fm.slot_mask & 0xf0) {
track->info = FMDRIVER_TRACK_INFO_FM3EX;
@@ -2814,20 +2847,13 @@ static void fmp_work_status_update(struct fmdriver_work *work,
}
track->volume = 0x7f - part->actual_vol;
}
- 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);
- track->tonenum = part->tone;
- track->detune = part->detune - ((part->detune & 0x8000) ? 0x10000 : 0);
- track->status[0] = part->lfo_f.p ? 'P' : '-';
- track->status[1] = part->lfo_f.q ? 'Q' : '-';
- track->status[2] = part->lfo_f.r ? 'R' : '-';
- track->status[3] = part->lfo_f.a ? 'A' : '-';
- track->status[4] = '-';
- track->status[5] = part->lfo_f.e ? 'e' : '-';
- track->status[6] = (part->type.fm && part->u.fm.hlfo_apms) ? 'H' : '-';
- track->status[7] = part->status.pitchbend ? 'P' : '-';
}
+ work->track_status[FMDRIVER_TRACK_PPZ8_7].playing = fmp->pdzf.rhythm[0].enabled || fmp->pdzf.rhythm[1].enabled;
+ work->track_status[FMDRIVER_TRACK_PPZ8_7].tonenum = work->ppz8 ? work->ppz8->channel[6].voice : 0;
+ uint8_t key = 0xff;
+ if (work->ppz8 && work->ppz8->channel[6].playing) key = fmp->pdzf.rhythm_current_note;
+ work->track_status[FMDRIVER_TRACK_PPZ8_7].key = key;
+ work->track_status[FMDRIVER_TRACK_PPZ8_7].actual_key = key;
}
static void fmp_part_pdzf_freq_update(
@@ -3147,6 +3173,11 @@ static void fmp_struct_init(struct fmdriver_work *work,
fmp->parts[FMP_PART_FM_EX3].pdzf.ppz8_channel = 5;
fmp->parts[FMP_PART_FM_EX3].pdzf.loopstart32 = -1;
fmp->parts[FMP_PART_FM_EX3].pdzf.loopend32 = -1;
+
+ // ppz8 unused parts
+ for (int i = 0; i < 8; i++) {
+ fmp->parts[FMP_PART_PPZ8_1+i].status.off = true;
+ }
}
// 1774
diff --git a/fmdriver/fmdriver_fmp.h b/fmdriver/fmdriver_fmp.h
index 4fd107a..ead8074 100644
--- a/fmdriver/fmdriver_fmp.h
+++ b/fmdriver/fmdriver_fmp.h
@@ -534,6 +534,7 @@ struct driver_fmp {
uint8_t note;
bool enabled;
} rhythm[2];
+ uint8_t rhythm_current_note;
} pdzf;
};
diff --git a/fmdriver/ppz8.c b/fmdriver/ppz8.c
index 4dea524..d1afd72 100644
--- a/fmdriver/ppz8.c
+++ b/fmdriver/ppz8.c
@@ -37,6 +37,7 @@ void ppz8_init(struct ppz8 *ppz8, uint16_t srate, uint16_t mix_volume) {
channel->vol = 8;
channel->pan = 5;
channel->voice = 0;
+ leveldata_init(&channel->leveldata);
}
ppz8->srate = srate;
ppz8->totalvol = 12;
@@ -128,6 +129,7 @@ static int32_t ppz8_channel_calc(struct ppz8 *ppz8, struct ppz8_channel *channel
}
void ppz8_mix(struct ppz8 *ppz8, int16_t *buf, unsigned samples) {
+ unsigned level[8] = {0};
static const uint8_t pan_vol[10][2] = {
{0, 0},
{4, 0},
@@ -144,11 +146,13 @@ void ppz8_mix(struct ppz8 *ppz8, int16_t *buf, unsigned samples) {
int32_t lo = buf[i*2+0];
int32_t ro = buf[i*2+1];
for (int p = 0; p < 8; p++) {
- //if (p < 3) continue;
- //if (p >= 6) continue;
struct ppz8_channel *channel = &ppz8->channel[p];
if (!channel->playing) continue;
int32_t out = ppz8_channel_calc(ppz8, channel);
+ {
+ unsigned uout = out > 0 ? out : -out;
+ if (uout > level[p]) level[p] = uout;
+ }
if ((1u << p) & (ppz8->mask)) continue;
out *= ppz8->mix_volume;
out >>= 15;
@@ -162,6 +166,9 @@ void ppz8_mix(struct ppz8 *ppz8, int16_t *buf, unsigned samples) {
buf[i*2+0] = lo;
buf[i*2+1] = ro;
}
+ for (int p = 0; p < 8; p++) {
+ leveldata_update(&ppz8->channel[p].leveldata, level[p]);
+ }
}
static int16_t calc_acc(int16_t acc, uint16_t adpcmd, uint8_t data) {
diff --git a/fmdriver/ppz8.h b/fmdriver/ppz8.h
index f4ef4cb..35c67dc 100644
--- a/fmdriver/ppz8.h
+++ b/fmdriver/ppz8.h
@@ -3,6 +3,8 @@
#include <stdint.h>
#include <stdbool.h>
+#include <stdatomic.h>
+#include "leveldata/leveldata.h"
#ifdef __cplusplus
extern "C" {
@@ -35,6 +37,7 @@ struct ppz8_channel {
uint8_t pan;
uint8_t voice;
bool playing;
+ struct leveldata leveldata;
};
struct ppz8 {
diff --git a/fmdsp/fmdsp.c b/fmdsp/fmdsp.c
index 7331dc2..16fe141 100644
--- a/fmdsp/fmdsp.c
+++ b/fmdsp/fmdsp.c
@@ -6,6 +6,8 @@
#include "libopna/opna.h"
#include "fmdsp_platform_info.h"
#include "version.h"
+#include <math.h>
+#include <string.h>
fmdsp_vramlookup_type fmdsp_vramlookup_func = fmdsp_vramlookup_c;
@@ -87,7 +89,7 @@ static void fmdsp_putline(const char *strptr, uint8_t *vram,
xo -= (xo % (fw*8));
cp932str++;
} else {
- if ((x+xo+8) > PC98_W) return;
+ if ((x+xo+fw) > PC98_W) return;
const void *fp = font->get(font, *cp932str++, FMDSP_FONT_ANK);
if (fp) {
vram_putchar(vram, fp, x+xo, y, fw, fh, color, bg);
@@ -102,7 +104,7 @@ static void fmdsp_putline(const char *strptr, uint8_t *vram,
uint8_t sjis_2nd = *cp932str++;
uint16_t jis = sjis2jis(sjis_1st, sjis_2nd);
bool half = jis_is_halfwidth(jis);
- if ((x+xo+(half ? 8 : 16)) > PC98_W) return;
+ if ((x+xo+fw*(half ? 1 : 2)) > PC98_W) return;
const void *fp = font->get(font, jis, FMDSP_FONT_JIS_LEFT);
if (fp) {
vram_putchar(vram, fp, x+xo, y, fw, fh, color, bg);
@@ -395,10 +397,35 @@ static void fmdsp_track_init_10(struct fmdsp *fmdsp,
for (int x = 0; x < 20; x++) {
vram[(SPECTRUM_Y+4)*PC98_W+SPECTRUM_X+240+2*x] = 1;
}
- fmdsp_putline("ON/OFF", vram, &font_fmdsp_small, LEVEL_TEXT_X, LEVEL_TEXT_Y, 1, true);
- fmdsp_putline("PANPOT", vram, &font_fmdsp_small, LEVEL_TEXT_X, LEVEL_TEXT_Y+8, 1, true);
- fmdsp_putline("PROGRAM", vram, &font_fmdsp_small, LEVEL_TEXT_X-5, LEVEL_TEXT_Y+16, 1, true);
- fmdsp_putline("KEYCODE", vram, &font_fmdsp_small, LEVEL_TEXT_X-5, LEVEL_TEXT_Y+23, 1, true);
+ fmdsp_putline("ON", vram, &font_fmdsp_small,
+ LEVEL_TEXT_X+5, LEVEL_TEXT_Y, 1, true);
+ fmdsp_putline("PAN", vram, &font_fmdsp_small,
+ LEVEL_TEXT_X, LEVEL_TEXT_Y+8, 1, true);
+ fmdsp_putline("PROG", vram, &font_fmdsp_small,
+ LEVEL_TEXT_X-5, LEVEL_TEXT_Y+16, 1, true);
+ fmdsp_putline("KEY", vram, &font_fmdsp_small,
+ LEVEL_TEXT_X, LEVEL_TEXT_Y+23, 1, true);
+ fmdsp_putline("FM1", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*0, LEVEL_TRACK_Y, 7, true);
+ fmdsp_putline("FM4", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*3, LEVEL_TRACK_Y, 7, true);
+ fmdsp_putline("SSG", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*6, LEVEL_TRACK_Y, 7, true);
+ fmdsp_putline("RHY", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*9, LEVEL_TRACK_Y, 7, true);
+ fmdsp_putline("ADP", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*10, LEVEL_TRACK_Y, 7, true);
+ fmdsp_putline("PPZ", vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*11, LEVEL_TRACK_Y, 7, true);
+ for (int y = 0; y < 63; y++) {
+ vram[(LEVEL_Y+y)*PC98_W+LEVEL_X-2] = 2;
+ if ((y % 2) == 0) vram[(LEVEL_Y+y)*PC98_W+LEVEL_X-3] = 2;
+ if ((y % 8) == 6) vram[(LEVEL_Y+y)*PC98_W+LEVEL_X-4] = 2;
+ }
+ fmdsp_putline("0", vram, &font_fmdsp_small,
+ LEVEL_X-9, LEVEL_Y-1, 7, true);
+ fmdsp_putline("-48", vram, &font_fmdsp_small,
+ LEVEL_X-19, LEVEL_Y+56, 7, true);
}
}
@@ -713,8 +740,8 @@ static void fmdsp_track_without_key(
}
static void fmdsp_update_10(struct fmdsp *fmdsp,
- const struct fmdriver_work *work,
- const struct opna *opna,
+ struct fmdriver_work *work,
+ struct opna *opna,
uint8_t *vram,
struct fmplayer_fft_input_data *idata) {
if (fmdsp->style != FMDSP_DISPSTYLE_ORIGINAL) {
@@ -939,6 +966,134 @@ static void fmdsp_update_10(struct fmdsp *fmdsp,
vram[py*PC98_W+px+1] = 7;
vram[py*PC98_W+px+2] = 7;
}
+ // level
+ struct {
+ unsigned level;
+ int t;
+ bool masked;
+ uint8_t pan;
+ uint8_t prog;
+ uint8_t key;
+ bool playing;
+ } levels[FMDSP_LEVEL_COUNT] = {0};
+ for (int c = 0; c < 6; c++) {
+ levels[c].level = leveldata_read(&opna->fm.channel[c].leveldata);
+ static const int table[4] = {5, 4, 0, 2};
+ levels[c].pan = table[opna->fm.lselect[c]*2 + opna->fm.rselect[c]];
+ }
+ levels[0].t = FMDRIVER_TRACK_FM_1;
+ levels[1].t = FMDRIVER_TRACK_FM_2;
+ levels[2].t = FMDRIVER_TRACK_FM_3;
+ levels[3].t = FMDRIVER_TRACK_FM_4;
+ levels[4].t = FMDRIVER_TRACK_FM_5;
+ levels[5].t = FMDRIVER_TRACK_FM_6;
+
+ for (int c = 0; c < 3; c++) {
+ levels[6+c].level = leveldata_read(&opna->resampler.leveldata[c]);
+ levels[6+c].t = FMDRIVER_TRACK_SSG_1+c;
+ levels[6+c].pan = 2;
+ }
+ {
+ unsigned dl = 0;
+ for (int d = 0; d < 6; d++) {
+ unsigned l = leveldata_read(&opna->drum.drums[d].leveldata);
+ if (l > dl) dl = l;
+ }
+ levels[9].level = dl;
+ levels[9].pan = 2;
+ }
+ levels[10].level = leveldata_read(&opna->adpcm.leveldata);
+ levels[10].t = FMDRIVER_TRACK_ADPCM;
+ {
+ static const int table[4] = {5, 4, 0, 2};
+ int ind = 0;
+ if (opna->adpcm.control2 & 0x80) ind |= 2;
+ if (opna->adpcm.control2 & 0x40) ind |= 1;
+ levels[10].pan = table[ind];
+ }
+ for (int p = 0; p < 8; p++) {
+ levels[11+p].pan = 5;
+ levels[11+p].t = FMDRIVER_TRACK_PPZ8_1+p;
+ }
+ if (work->ppz8) {
+ for (int p = 0; p < 8; p++) {
+ levels[11+p].level = leveldata_read(&work->ppz8->channel[p].leveldata);
+ static const int table[10] = {5, 0, 1, 1, 1, 2, 3, 3, 3, 4};
+ levels[11+p].pan = table[work->ppz8->channel[p].pan];
+ }
+ }
+ for (int c = 0; c < FMDSP_LEVEL_COUNT; c++) {
+ levels[c].masked = c == 9 ? fmdsp->masked_rhythm : fmdsp->masked[levels[c].t];
+ levels[c].prog = work->track_status[levels[c].t].tonenum;
+ levels[c].key = work->track_status[levels[c].t].key;
+ levels[c].playing = work->track_status[levels[c].t].playing;
+ if (work->track_status[levels[c].t].info == FMDRIVER_TRACK_INFO_PDZF ||
+ work->track_status[levels[c].t].info == FMDRIVER_TRACK_INFO_PPZ8) {
+ levels[c].playing = false;
+ }
+ if (!levels[c].playing) levels[c].pan = 5;
+ }
+
+ for (int c = 0; c < FMDSP_LEVEL_COUNT; c++) {
+ unsigned level = levels[c].level;
+ unsigned llevel = 0;
+ if (level) {
+ float db = 20.0f * log10f((float)level / (1<<15));
+ float fllevel = (db / 48.0f + 1.0f) * 32.0f;
+ if (fllevel > 0.0f) llevel = fllevel;
+ }
+
+ if (fmdsp->leveldata[c] <= llevel) {
+ fmdsp->leveldata[c] = llevel;
+ fmdsp->levelcnt[c] = 30;
+ } else {
+ if (fmdsp->levelcnt[c]) {
+ fmdsp->levelcnt[c]--;
+ } else {
+ if (fmdsp->leveldata[c]) {
+ if (fmdsp->leveldropdiv[c]) {
+ fmdsp->leveldropdiv[c]--;
+ } else {
+ static const uint8_t divtab[16] = {
+ 32, 16, 8, 8, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2,
+ };
+ fmdsp->leveldropdiv[c] = divtab[fmdsp->leveldata[c] / 2];
+ fmdsp->leveldata[c]--;
+ }
+ }
+ }
+ }
+ for (unsigned y = 0; y < 64; y += 2) {
+ unsigned plevel = (63 - y) / 2;
+ for (int x = 0; x < LEVEL_DISP_W; x++) {
+ uint8_t color = llevel > plevel ? 2 : 3;
+ if (plevel == fmdsp->leveldata[c]) color = 7;
+ vram[(y+LEVEL_Y)*PC98_W+LEVEL_X+LEVEL_W*c+x] = color;
+ }
+ }
+ vramblit_color(vram,
+ LEVEL_X+LEVEL_W*c-1, PANPOT_Y,
+ s_panpot[levels[c].pan], PANPOT_W, PANPOT_H,
+ levels[c].masked ? 5 : 1);
+ char buf[4];
+ if (c != 9) {
+ snprintf(buf, sizeof(buf), "%03d", levels[c].prog);
+ fmdsp_putline(buf, vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*c, LEVEL_PROG_Y,
+ 1, true);
+ }
+ strcpy(buf, "---");
+ if (c != 9 && levels[c].playing) {
+ uint8_t oct = levels[c].key >> 4;
+ uint8_t n = levels[c].key & 0xf;
+ if (n < 12) {
+ snprintf(buf, sizeof(buf), "%03d", oct*12+n);
+ }
+ }
+ fmdsp_putline(buf, vram, &font_fmdsp_small,
+ LEVEL_X+LEVEL_W*c, LEVEL_KEY_Y,
+ 1, true);
+ }
}
}
static void fmdsp_update_13(struct fmdsp *fmdsp,
@@ -997,8 +1152,8 @@ static void fmdsp_update_13(struct fmdsp *fmdsp,
}
void fmdsp_update(struct fmdsp *fmdsp,
- const struct fmdriver_work *work,
- const struct opna *opna,
+ struct fmdriver_work *work,
+ struct opna *opna,
uint8_t *vram,
struct fmplayer_fft_input_data *idata) {
if (fmdsp->style_updated) {
@@ -1022,6 +1177,7 @@ void fmdsp_update(struct fmdsp *fmdsp,
fmdsp->masked[FMDRIVER_TRACK_SSG_2] = mask & LIBOPNA_CHAN_SSG_2;
fmdsp->masked[FMDRIVER_TRACK_SSG_3] = mask & LIBOPNA_CHAN_SSG_3;
fmdsp->masked[FMDRIVER_TRACK_ADPCM] = mask & LIBOPNA_CHAN_ADPCM;
+ fmdsp->masked_rhythm = (mask & LIBOPNA_CHAN_DRUM_ALL) == LIBOPNA_CHAN_DRUM_ALL;
unsigned ppz8mask = 0;
if (work->ppz8) {
ppz8mask = ppz8_get_mask(work->ppz8);
diff --git a/fmdsp/fmdsp.h b/fmdsp/fmdsp.h
index 6a98707..e458d39 100644
--- a/fmdsp/fmdsp.h
+++ b/fmdsp/fmdsp.h
@@ -19,7 +19,8 @@ enum {
};
enum {
- FMDSP_PALETTE_COLORS = 10
+ FMDSP_PALETTE_COLORS = 10,
+ FMDSP_LEVEL_COUNT = 19,
};
enum FMDSP_DISPSTYLE {
@@ -38,12 +39,16 @@ struct fmdsp {
enum FMDSP_DISPSTYLE style;
bool style_updated;
bool masked[FMDRIVER_TRACK_NUM];
+ bool masked_rhythm;
uint8_t fftdata[FFTDISPLEN];
uint8_t fftcnt[FFTDISPLEN];
uint8_t fftdropdiv[FFTDISPLEN];
uint64_t framecnt;
int cpuusage;
int fps;
+ uint8_t leveldata[FMDSP_LEVEL_COUNT];
+ uint8_t levelcnt[FMDSP_LEVEL_COUNT];
+ uint8_t leveldropdiv[FMDSP_LEVEL_COUNT];
};
struct fmdriver_work;
@@ -51,8 +56,8 @@ void fmdsp_init(struct fmdsp *fmdsp, const struct fmdsp_font *font);
void fmdsp_vram_init(struct fmdsp *fmdsp,
struct fmdriver_work *work,
uint8_t *vram);
-void fmdsp_update(struct fmdsp *fmdsp, const struct fmdriver_work *work,
- const struct opna *opna, uint8_t *vram,
+void fmdsp_update(struct fmdsp *fmdsp, struct fmdriver_work *work,
+ struct opna *opna, uint8_t *vram,
struct fmplayer_fft_input_data *idata
);
void fmdsp_vrampalette(struct fmdsp *fmdsp, const uint8_t *vram, uint8_t *vram32, int stride);
diff --git a/fmdsp/fmdsp_sprites.h b/fmdsp/fmdsp_sprites.h
index d9d71ac..c942e24 100644
--- a/fmdsp/fmdsp_sprites.h
+++ b/fmdsp/fmdsp_sprites.h
@@ -14,8 +14,8 @@ enum {
TDETAIL_GT_X = TDETAIL_VL_V_X+19,
TDETAIL_GT_V_X = TDETAIL_GT_X+13,
TDETAIL_DT_X = TDETAIL_GT_V_X+23,
- TDETAIL_DT_S_X = TDETAIL_DT_X+12,
- TDETAIL_DT_V_X = TDETAIL_DT_S_X+5,
+ TDETAIL_DT_S_X = TDETAIL_DT_X+13,
+ TDETAIL_DT_V_X = TDETAIL_DT_S_X+4,
TDETAIL_M_X = 249,
TDETAIL_M_V_X = TDETAIL_M_X+8,
NUM_X = 31,
@@ -166,6 +166,16 @@ enum {
FLOPPY_Y = 87,
LEVEL_TEXT_X = 318,
LEVEL_TEXT_Y = 290,
+ LEVEL_X = 353-16,
+ LEVEL_Y = 227,
+ LEVEL_DISP_W = 14,
+ LEVEL_W = 16,
+ PANPOT_W = 15,
+ PANPOT_H = 15,
+ PANPOT_Y = LEVEL_Y+64,
+ LEVEL_TRACK_Y = LEVEL_Y-9,
+ LEVEL_PROG_Y = PANPOT_Y+15,
+ LEVEL_KEY_Y = LEVEL_PROG_Y+7,
};
enum {
@@ -867,3 +877,108 @@ static uint8_t s_floppy[FLOPPY_W*FLOPPY_H] = {
0,0,0,0,3,3,3,0,0,0,3,3,3,3,0,0,3,3,3,3,0,3,0,0,0,0,0,0,3,3,3,0,0,3,0,0,0,3,
0,0,3,3,3,0,0,3,3,3,3,3,0,0,3,3,3,0,0,0,0,0,3,0,
};
+
+static uint8_t s_panpot[6][PANPOT_W*PANPOT_H] = {
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,1,0,1,1,1,0,0,0,0,0,0,0,1,0,
+ 0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,
+ 1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,
+ 0,1,0,0,0,1,1,1,1,0,0,0,0,1,0,
+ 0,1,0,0,1,1,1,0,0,0,0,0,0,1,0,
+ 1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,
+ 0,1,0,0,0,1,1,1,1,1,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,
+ 0,1,0,0,0,0,1,1,1,1,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,1,1,1,0,0,1,0,
+ 1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,1,0,0,0,0,0,0,0,1,1,1,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+ {
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,
+ 0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
+ 0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,
+ 0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,
+ },
+};
diff --git a/libopna/opnaadpcm.c b/libopna/opnaadpcm.c
index 68dbae5..361064a 100644
--- a/libopna/opnaadpcm.c
+++ b/libopna/opnaadpcm.c
@@ -35,6 +35,7 @@ void opna_adpcm_reset(struct opna_adpcm *adpcm) {
adpcm->prev_acc = 0;
adpcm->adpcmd = 127;
adpcm->out = 0;
+ leveldata_init(&adpcm->leveldata);
}
static uint32_t addr_conv(const struct opna_adpcm *adpcm, uint16_t a) {
@@ -183,8 +184,14 @@ void opna_adpcm_writereg(struct opna_adpcm *adpcm, unsigned reg, unsigned val) {
void opna_adpcm_mix(struct opna_adpcm *adpcm, int16_t *buf, unsigned samples) {
if (!adpcm->ram) return;
if (!(adpcm->control1 & C1_START)) return;
+ unsigned level = 0;
for (unsigned i = 0; i < samples; i++) {
adpcm_calc(adpcm);
+ {
+ int clevel = adpcm->out>>1;
+ if (clevel < 0) clevel = -clevel;
+ if (((unsigned)clevel) > level) level = clevel;
+ }
if (!adpcm->masked) {
int32_t lo = buf[i*2+0];
int32_t ro = buf[i*2+1];
@@ -199,6 +206,7 @@ void opna_adpcm_mix(struct opna_adpcm *adpcm, int16_t *buf, unsigned samples) {
}
if (!(adpcm->control1 & C1_START)) return;
}
+ leveldata_update(&adpcm->leveldata, level);
}
void opna_adpcm_set_ram_256k(struct opna_adpcm *adpcm, void *ram) {
diff --git a/libopna/opnaadpcm.h b/libopna/opnaadpcm.h
index 42033ad..27a0be3 100644
--- a/libopna/opnaadpcm.h
+++ b/libopna/opnaadpcm.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "leveldata/leveldata.h"
#ifdef __cplusplus
extern "C" {
@@ -24,6 +25,8 @@ struct opna_adpcm {
uint16_t adpcmd;
int16_t out;
bool masked;
+ atomic_uint levelvu;
+ struct leveldata leveldata;
};
void opna_adpcm_reset(struct opna_adpcm *adpcm);
diff --git a/libopna/opnadrum.c b/libopna/opnadrum.c
index 1a298af..c3e8d38 100644
--- a/libopna/opnadrum.c
+++ b/libopna/opnadrum.c
@@ -23,6 +23,7 @@ void opna_drum_reset(struct opna_drum *drum) {
drum->drums[d].level = 0;
drum->drums[d].left = false;
drum->drums[d].right = false;
+ leveldata_init(&drum->drums[d].leveldata);
}
drum->total_level = 0;
drum->mask = 0;
@@ -81,6 +82,7 @@ void opna_drum_set_rom(struct opna_drum *drum, void *romptr) {
}
void opna_drum_mix(struct opna_drum *drum, int16_t *buf, int samples) {
+ unsigned levels[6] = {0};
for (int i = 0; i < samples; i++) {
int32_t lo = buf[i*2+0];
int32_t ro = buf[i*2+1];
@@ -91,6 +93,8 @@ void opna_drum_mix(struct opna_drum *drum, int16_t *buf, int samples) {
unsigned level = (drum->drums[d].level^0x1f) + (drum->total_level^0x3f);
co *= 15 - (level&7);
co >>= 1+(level>>3);
+ unsigned outlevel = co > 0 ? co : -co;
+ if (outlevel > levels[d]) levels[d] = outlevel;
if (!(drum->mask & (1u << d))) {
if (drum->drums[d].left) lo += co;
if (drum->drums[d].right) ro += co;
@@ -109,6 +113,9 @@ void opna_drum_mix(struct opna_drum *drum, int16_t *buf, int samples) {
buf[i*2+0] = lo;
buf[i*2+1] = ro;
}
+ for (int d = 0; d < 6; d++) {
+ leveldata_update(&drum->drums[d].leveldata, levels[d]);
+ }
}
void opna_drum_writereg(struct opna_drum *drum, unsigned reg, unsigned val) {
diff --git a/libopna/opnadrum.h b/libopna/opnadrum.h
index 79f95a2..7260995 100644
--- a/libopna/opnadrum.h
+++ b/libopna/opnadrum.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "leveldata/leveldata.h"
#ifdef __cplusplus
extern "C" {
@@ -32,6 +33,7 @@ struct opna_drum {
unsigned level;
bool left;
bool right;
+ struct leveldata leveldata;
} drums[6];
unsigned total_level;
int16_t rom_bd[OPNA_ROM_BD_SIZE];
diff --git a/libopna/opnafm.c b/libopna/opnafm.c
index 4e86b80..0d57a98 100644
--- a/libopna/opnafm.c
+++ b/libopna/opnafm.c
@@ -3,6 +3,7 @@
#include "opnatables.h"
+
//#include <stdio.h>
#define printf(...)
@@ -27,6 +28,7 @@ static void opna_fm_slot_reset(struct opna_fm_slot *slot) {
void opna_fm_chan_reset(struct opna_fm_channel *chan) {
+ leveldata_init(&chan->leveldata);
for (int i = 0; i < 4; i++) {
opna_fm_slot_reset(&chan->slot[i]);
}
@@ -595,8 +597,6 @@ static int gcd(int a, int b) {
return b;
}
-
-
void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples,
struct oscillodata *oscillo, unsigned offset) {
if (oscillo) {
@@ -620,6 +620,7 @@ void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples,
}
}
}
+ unsigned level[6] = {0};
for (unsigned i = 0; i < samples; i++) {
if (!fm->env_div3) {
for (int c = 0; c < 6; c++) {
@@ -639,6 +640,11 @@ void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples,
for (int c = 0; c < 6; c++) {
struct opna_fm_frame o = opna_fm_chanout(&fm->channel[c]);
+ unsigned nlevel[2];
+ nlevel[0] = o.data[0] > 0 ? o.data[0] : -o.data[0];
+ nlevel[1] = o.data[1] > 0 ? o.data[1] : -o.data[1];
+ if (nlevel[1] > nlevel[0]) nlevel[0] = nlevel[1];
+ if (nlevel[0] > level[c]) level[c] = nlevel[0];
if (oscillo) oscillo[c].buf[offset+i] = o.data[0] + o.data[1];
// TODO: CSM
if (c == 2 && fm->ch3.mode != CH3_MODE_NORMAL) {
@@ -677,4 +683,7 @@ void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples,
}
fm->env_div3--;
}
+ for (int c = 0; c < 6; c++) {
+ leveldata_update(&fm->channel[c].leveldata, level[c]);
+ }
}
diff --git a/libopna/opnafm.h b/libopna/opnafm.h
index 9da143f..359517b 100644
--- a/libopna/opnafm.h
+++ b/libopna/opnafm.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "leveldata/leveldata.h"
#ifdef __cplusplus
extern "C" {
@@ -64,6 +65,8 @@ struct opna_fm_channel {
uint8_t fb;
uint16_t fnum;
uint8_t blk;
+
+ struct leveldata leveldata;
};
struct opna_fm {
diff --git a/libopna/opnassg.c b/libopna/opnassg.c
index 86d9c9a..d92a8b8 100644
--- a/libopna/opnassg.c
+++ b/libopna/opnassg.c
@@ -134,6 +134,9 @@ void opna_ssg_resampler_reset(struct opna_ssg_resampler *resampler) {
resampler->buf[i] = 0;
}
resampler->index = 0;
+ for (int c = 0; c < 3; c++) {
+ leveldata_init(&resampler->leveldata[c]);
+ }
}
void opna_ssg_writereg(struct opna_ssg *ssg, unsigned reg, unsigned val) {
@@ -286,6 +289,7 @@ void opna_ssg_mix_55466(
}
}
}
+ unsigned level[3] = {0};
for (int i = 0; i < samples; i++) {
{
int ssg_samples = ((resampler->index + 9)>>1) - ((resampler->index)>>1);
@@ -305,6 +309,12 @@ void opna_ssg_mix_55466(
opna_ssg_sinc_calc_func(resampler->index, resampler->buf, outbuf);
for (int ch = 0; ch < 3; ch++) {
if (oscillo) oscillo[ch].buf[offset+i] = outbuf[ch] >> 15;
+ int32_t nlevel = outbuf[ch];
+ nlevel >>= 16;
+ nlevel *= 13000;
+ nlevel >>= 14;
+ if (nlevel < 0) nlevel = -nlevel;
+ if (((unsigned)nlevel) > level[ch]) level[ch] = nlevel;
if (!(ssg->mask & (1<<ch))) sample += outbuf[ch] >> 2;
}
sample >>= 16;
@@ -322,5 +332,8 @@ void opna_ssg_mix_55466(
buf[i*2+0] = lo;
buf[i*2+1] = ro;
}
+ for (int c = 0; c < 3; c++) {
+ leveldata_update(&resampler->leveldata[c], level[c]);
+ }
}
#undef BUFINDEX
diff --git a/libopna/opnassg.h b/libopna/opnassg.h
index 8af8429..3077d08 100644
--- a/libopna/opnassg.h
+++ b/libopna/opnassg.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "leveldata/leveldata.h"
#ifdef __cplusplus
extern "C" {
@@ -35,6 +36,7 @@ struct opna_ssg {
struct opna_ssg_resampler {
int16_t buf[OPNA_SSG_SINCTABLELEN*4 * 2];
unsigned index;
+ struct leveldata leveldata[3];
};
void opna_ssg_reset(struct opna_ssg *ssg);