aboutsummaryrefslogtreecommitdiff
path: root/utils/gen_adldata/measurer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/gen_adldata/measurer.cpp')
-rw-r--r--utils/gen_adldata/measurer.cpp722
1 files changed, 665 insertions, 57 deletions
diff --git a/utils/gen_adldata/measurer.cpp b/utils/gen_adldata/measurer.cpp
index dc73363..4663072 100644
--- a/utils/gen_adldata/measurer.cpp
+++ b/utils/gen_adldata/measurer.cpp
@@ -1,6 +1,11 @@
#include "measurer.h"
+#include "file_formats/common.h"
#include <cmath>
+#ifdef GEN_ADLDATA_DEEP_DEBUG
+#include "../midiplay/wave_writer.h"
+#endif
+
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
@@ -16,6 +21,56 @@
# include "../../src/chips/dosbox_opl3.h"
#endif
+#define NUM_OF_CHANNELS 23
+#define NUM_OF_RM_CHANNELS 5
+
+//! Per-channel and per-operator registers map
+static const uint16_t g_operatorsMap[(NUM_OF_CHANNELS + NUM_OF_RM_CHANNELS) * 2] =
+{
+ // Channels 0-2
+ 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5
+ // Channels 3-5
+ 0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11
+ // Channels 6-8
+ 0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17
+ // Same for second card
+ 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23
+ 0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29
+ 0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35
+
+ //==For Rhythm-mode percussions
+ // Channel 18
+ 0x010, 0x013, // operators 12,15
+ // Channel 19
+ 0xFFF, 0x014, // operator 16
+ // Channel 19
+ 0x012, 0xFFF, // operator 14
+ // Channel 19
+ 0xFFF, 0x015, // operator 17
+ // Channel 19
+ 0x011, 0xFFF, // operator 13
+
+ //==For Rhythm-mode percussions in CMF, snare and cymbal operators has inverted!
+ 0x010, 0x013, // operators 12,15
+ // Channel 19
+ 0x014, 0xFFF, // operator 16
+ // Channel 19
+ 0x012, 0xFFF, // operator 14
+ // Channel 19
+ 0x015, 0xFFF, // operator 17
+ // Channel 19
+ 0x011, 0xFFF // operator 13
+};
+
+//! Channel map to regoster offsets
+static const uint16_t g_channelsMap[NUM_OF_CHANNELS] =
+{
+ 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8
+ 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set)
+ 0x006, 0x007, 0x008, 0x008, 0x008 // <- hw percussions, hihats and cymbals using tom-tom's channel as pitch source
+};
+
+
template <class T>
class AudioHistory
{
@@ -87,24 +142,37 @@ struct TinySynth
{
OPLChipBase *m_chip;
unsigned m_notesNum;
- int m_notenum;
- int8_t m_fineTune;
+ unsigned m_actualNotesNum;
+ bool m_isReal4op;
+ bool m_isPseudo4op;
+ bool m_isRhythmMode;
+ int m_playNoteNum;
+ int8_t m_voice1Detune;
int16_t m_noteOffsets[2];
unsigned m_x[2];
+ bool m_isSilentGuess;
+
+ void writeReg(uint16_t addr, uint8_t data)
+ {
+ m_chip->writeReg(addr, data);
+ }
+
void resetChip()
{
- static const short initdata[(2 + 3 + 2 + 2) * 2] =
+ static const short initdata[] =
{
- 0x004, 96, 0x004, 128, // Pulse timer
- 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable, leave disabled
- 0x001, 32, 0x0BD, 0 // Enable wave & melodic
+ 0x004, 96, 0x004, 128, // Pulse timer
+ 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable
+ 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions
};
m_chip->setRate(g_outputRate);
- for(unsigned a = 0; a < 18; a += 2)
- m_chip->writeReg((uint16_t)initdata[a], (uint8_t)initdata[a + 1]);
+ for(size_t a = 0; a < 18; ++a)
+ writeReg(0xB0 + g_channelsMap[a], 0x00);
+ for(unsigned a = 0; a < 14; a += 2)
+ writeReg((uint16_t)initdata[a], (uint8_t)initdata[a + 1]);
}
void setInstrument(const ins &in)
@@ -130,22 +198,22 @@ struct TinySynth
}
std::memset(m_x, 0, sizeof(m_x));
- m_notenum = in.notenum >= 128 ? (in.notenum - 128) : in.notenum;
- if(m_notenum == 0)
- m_notenum = 25;
+ m_isRhythmMode = false;
+ m_playNoteNum = in.notenum >= 128 ? (in.notenum - 128) : in.notenum;
+ m_isReal4op = in.real4op && !in.pseudo4op;
+ m_isPseudo4op = in.pseudo4op;
+ if(m_playNoteNum == 0)
+ m_playNoteNum = 25;
m_notesNum = in.insno1 == in.insno2 ? 1 : 2;
- m_fineTune = 0;
+ m_actualNotesNum = (m_isReal4op ? 1 : m_notesNum);
+ m_voice1Detune = 0;
m_noteOffsets[0] = rawData[0].finetune;
m_noteOffsets[1] = rawData[1].finetune;
if(in.pseudo4op)
- m_fineTune = in.voice2_fine_tune;
- if(in.real4op)
- {
- m_chip->writeReg(0x105, 1);
- m_chip->writeReg(0x104, 0xFF);
- }
+ m_voice1Detune = in.voice2_fine_tune;
+ writeReg(0x104, in.real4op ? (1 << 6) - 1 : 0x00);
- //For clearer measurement, disable tremolo and vibrato
+ //For cleaner measurement, disable tremolo and vibrato
rawData[0].data[0] &= 0x3F;
rawData[0].data[1] &= 0x3F;
rawData[1].data[0] &= 0x3F;
@@ -156,25 +224,88 @@ struct TinySynth
static const unsigned char patchdata[11] =
{0x20, 0x23, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0x40, 0x43, 0xC0};
for(unsigned a = 0; a < 10; ++a)
- m_chip->writeReg(patchdata[a] + n * 8, rawData[n].data[a]);
- m_chip->writeReg(patchdata[10] + n * 8, rawData[n].data[10] | 0x30);
+ writeReg(patchdata[a] + n * 8, rawData[n].data[a]);
+ writeReg(patchdata[10] + n * 8, rawData[n].data[10] | 0x30);
}
}
- void noteOn()
+ void setInstrument(const BanksDump &db, const BanksDump::InstrumentEntry &ins)
{
+ bool isPseudo4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_Pseudo4op) != 0);
+ bool is4ops = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_Ins_4op) != 0) && !isPseudo4ops;
+ m_isRhythmMode = ((ins.instFlags & BanksDump::InstrumentEntry::WOPL_RhythmModeMask) != 0);
+ size_t opsNum = (is4ops || isPseudo4ops) ? 4 : 2;
+ BanksDump::Operator ops[4];
+ assert(ins.ops[0] >= 0);
+ assert(ins.ops[1] >= 0);
+ ops[0] = db.operators[ins.ops[0]];
+ ops[1] = db.operators[ins.ops[1]];
+ if(opsNum > 2)
+ {
+ assert(ins.ops[2] >= 0);
+ assert(ins.ops[3] >= 0);
+ ops[2] = db.operators[ins.ops[2]];
+ ops[3] = db.operators[ins.ops[3]];
+ }
+
std::memset(m_x, 0, sizeof(m_x));
+ m_playNoteNum = ins.percussionKeyNumber >= 128 ? (ins.percussionKeyNumber - 128) : ins.percussionKeyNumber;
+ m_isReal4op = is4ops;
+ m_isPseudo4op = isPseudo4ops;
+ if(m_playNoteNum == 0)
+ m_playNoteNum = 25;//60;
+ m_notesNum = opsNum / 2;
+ m_actualNotesNum = (m_isReal4op ? 1 : m_notesNum);
+ m_voice1Detune = 0;
+ m_noteOffsets[0] = ins.noteOffset1;
+ m_noteOffsets[1] = ins.noteOffset2;
+ if(isPseudo4ops)
+ m_voice1Detune = ins.secondVoiceDetune;
+ writeReg(0x104, m_isReal4op ? 0x3F : 0x00);
+
+ //For cleaner measurement, disable tremolo and vibrato
+ ops[0].d_E862 &= 0xFFFFFF3F;
+ ops[1].d_E862 &= 0xFFFFFF3F;
+ ops[2].d_E862 &= 0xFFFFFF3F;
+ ops[3].d_E862 &= 0xFFFFFF3F;
+
for(unsigned n = 0; n < m_notesNum; ++n)
{
- double hertz = 172.00093 * std::exp(0.057762265 * (m_notenum + m_noteOffsets[n]));
+ static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0};
+ size_t opOffset = (n * 2);
+ size_t opMapOffset = m_isReal4op ? (n * 6) : opOffset;
+ uint16_t op1off = g_operatorsMap[opMapOffset + 0];
+ uint16_t op2off = g_operatorsMap[opMapOffset + 1];
+ uint_fast32_t x1 = ops[opOffset + 0].d_E862, y1 = ops[opOffset + 1].d_E862;
+ uint_fast8_t x2 = ops[opOffset + 0].d_40, y2 = ops[opOffset + 1].d_40;
+ uint_fast8_t fbConn = (ins.fbConn >> (n * 8)) & 0xFF;
+
+ for(size_t a = 0; a < 4; ++a, x1 >>= 8, y1 >>= 8)
+ {
+ writeReg(data[a] + op1off, x1 & 0xFF);
+ writeReg(data[a] + op2off, y1 & 0xFF);
+ }
+ writeReg(0xC0 + g_channelsMap[m_isReal4op ? (n * 3) : n], fbConn | 0x30);
+
+ writeReg(0x40 + op1off, x2 & 0xFF);
+ writeReg(0x40 + op2off, y2 & 0xFF);
+ }
+ }
+
+ void noteOn()
+ {
+ std::memset(m_x, 0, sizeof(m_x));
+ for(unsigned n = 0; n < m_actualNotesNum; ++n)
+ {
+ double hertz = 172.00093 * std::exp(0.057762265 * (m_playNoteNum + m_noteOffsets[n]));
if(hertz > 131071)
{
std::fprintf(stdout, "%s:%d:0: warning: Why does note %d + note-offset %d produce hertz %g?\n", __FILE__, __LINE__,
- m_notenum, m_noteOffsets[n], hertz);
+ m_playNoteNum, m_noteOffsets[n], hertz);
std::fflush(stdout);
hertz = 131071;
}
- m_x[n] = 0x2000;
+ m_x[n] = 0x2000u;
while(hertz >= 1023.5)
{
hertz /= 2.0; // Calculate octave
@@ -183,16 +314,16 @@ struct TinySynth
m_x[n] += (unsigned int)(hertz + 0.5);
// Keyon the note
- m_chip->writeReg(0xA0 + n * 3, m_x[n] & 0xFF);
- m_chip->writeReg(0xB0 + n * 3, m_x[n] >> 8);
+ writeReg(0xA0 + g_channelsMap[n], m_x[n] & 0xFF);
+ writeReg(0xB0 + g_channelsMap[n], (m_x[n] >> 8) & 0xFF);
}
}
void noteOff()
{
// Keyoff the note
- for(unsigned n = 0; n < m_notesNum; ++n)
- m_chip->writeReg(0xB0 + n * 3, (m_x[n] >> 8) & 0xDF);
+ for(unsigned n = 0; n < m_actualNotesNum; ++n)
+ writeReg(0xB0 + g_channelsMap[n], (m_x[n] >> 8) & 0xDF);
}
void generate(int16_t *output, size_t frames)
@@ -392,8 +523,278 @@ DurationInfo MeasureDurations(const ins &in, OPLChipBase *chip)
return result;
}
+
+DurationInfo MeasureDurations(BanksDump &db, const BanksDump::InstrumentEntry &ins, OPLChipBase *chip)
+{
+ AudioHistory<double> audioHistory;
+
+ const unsigned interval = 150;
+ const unsigned samples_per_interval = g_outputRate / interval;
+
+ const double historyLength = 0.1; // maximum duration to memorize (seconds)
+ audioHistory.reset(std::ceil(historyLength * g_outputRate));
+
+ std::unique_ptr<double[]> window;
+ window.reset(new double[audioHistory.capacity()]);
+ unsigned winsize = 0;
+
+ TinySynth synth;
+ synth.m_chip = chip;
+ synth.resetChip();
+ synth.setInstrument(db, ins);
+ synth.noteOn();
+
+ if(synth.m_isRhythmMode) // Skip rhythm-mode check
+ {
+ DurationInfo result;
+ std::memset(&result, 0, sizeof(DurationInfo));
+ result.ms_sound_kon = 1000;
+ result.ms_sound_koff = 10;
+ result.nosound = false;
+ db.instruments[ins.instId].delay_on_ms = result.ms_sound_kon;
+ db.instruments[ins.instId].delay_off_ms = result.ms_sound_koff;
+ return result;
+ }
+
+#ifdef GEN_ADLDATA_DEEP_DEBUG
+ /*****************DEBUG******************/
+ char waveFileOut[80] = "";
+ std::snprintf(waveFileOut, 80, "fm_banks/_deep_debug/%04lu_%s_%u_an_%u_no.wav",
+ ins.instId, synth.m_isPseudo4op ? "pseudo4op" :
+ synth.m_isReal4op ? "4op" : "2op",
+ synth.m_actualNotesNum,
+ synth.m_notesNum);
+ void *waveCtx = ctx_wave_open(g_outputRate, waveFileOut);
+ ctx_wave_enable_stereo(waveCtx);
+ /*****************DEBUG******************/
+#endif
+
+ /* For capturing */
+ const unsigned max_silent = 6;
+ const unsigned max_on = 40;
+ const unsigned max_off = 60;
+
+ unsigned max_period_on = max_on * interval;
+ unsigned max_period_off = max_off * interval;
+
+ const double min_coefficient_on = 0.008;
+ const double min_coefficient_off = 0.2;
+
+ unsigned windows_passed_on = 0;
+ unsigned windows_passed_off = 0;
+
+ /* For Analyze the results */
+ double begin_amplitude = 0;
+ double peak_amplitude_value = 0;
+ size_t peak_amplitude_time = 0;
+ size_t quarter_amplitude_time = max_period_on;
+ bool quarter_amplitude_time_found = false;
+ size_t keyoff_out_time = 0;
+ bool keyoff_out_time_found = false;
+
+ const size_t audioBufferLength = 256;
+ const size_t audioBufferSize = 2 * audioBufferLength;
+ int16_t audioBuffer[audioBufferSize];
+
+ // For up to 40 seconds, measure mean amplitude.
+ double highest_sofar = 0;
+ short sound_min = 0, sound_max = 0;
+
+ for(unsigned period = 0; period < max_period_on; ++period, ++windows_passed_on)
+ {
+ for(unsigned i = 0; i < samples_per_interval;)
+ {
+ size_t blocksize = samples_per_interval - i;
+ blocksize = (blocksize < audioBufferLength) ? blocksize : audioBufferLength;
+ synth.generate(audioBuffer, blocksize);
+#ifdef GEN_ADLDATA_DEEP_DEBUG
+ /***************DEBUG******************/
+ ctx_wave_write(waveCtx, audioBuffer, blocksize * 2);
+ /***************DEBUG******************/
+#endif
+ for (unsigned j = 0; j < blocksize; ++j)
+ {
+ int16_t s = audioBuffer[2 * j];
+ audioHistory.add(s);
+ if(sound_min > s) sound_min = s;
+ if(sound_max < s) sound_max = s;
+ }
+ i += blocksize;
+ }
+
+ if(winsize != audioHistory.size())
+ {
+ winsize = audioHistory.size();
+ HannWindow(window.get(), winsize);
+ }
+
+ double rms = MeasureRMS(audioHistory.data(), window.get(), winsize);
+ /* ======== Peak time detection ======== */
+ if(period == 0)
+ {
+ begin_amplitude = rms;
+ peak_amplitude_value = rms;
+ peak_amplitude_time = 0;
+ }
+ else if(rms > peak_amplitude_value)
+ {
+ peak_amplitude_value = rms;
+ peak_amplitude_time = period;
+ // In next step, update the quater amplitude time
+ quarter_amplitude_time_found = false;
+ }
+ else if(!quarter_amplitude_time_found && (rms <= peak_amplitude_value * min_coefficient_on))
+ {
+ quarter_amplitude_time = period;
+ quarter_amplitude_time_found = true;
+ }
+ /* ======== Peak time detection =END==== */
+ if(rms > highest_sofar)
+ highest_sofar = rms;
+
+ if((period > max_silent * interval) &&
+ ( (rms < highest_sofar * min_coefficient_on) || (sound_min >= -1 && sound_max <= 1) )
+ )
+ break;
+ }
+
+ if(!quarter_amplitude_time_found)
+ quarter_amplitude_time = windows_passed_on;
+
+ if(windows_passed_on >= max_period_on)
+ {
+ // Just Keyoff the note
+ synth.noteOff();
+ }
+ else
+ {
+ // Reset the emulator and re-run the "ON" simulation until reaching the peak time
+ synth.resetChip();
+ synth.setInstrument(db, ins);
+ synth.noteOn();
+
+ audioHistory.reset(std::ceil(historyLength * g_outputRate));
+ for(unsigned period = 0;
+ ((period < peak_amplitude_time) || (period == 0)) && (period < max_period_on);
+ ++period)
+ {
+ for(unsigned i = 0; i < samples_per_interval;)
+ {
+ size_t blocksize = samples_per_interval - i;
+ blocksize = (blocksize < audioBufferLength) ? blocksize : audioBufferLength;
+ synth.generate(audioBuffer, blocksize);
+ for (unsigned j = 0; j < blocksize; ++j)
+ audioHistory.add(audioBuffer[2 * j]);
+ i += blocksize;
+ }
+ }
+ synth.noteOff();
+ }
+
+ // Now, for up to 60 seconds, measure mean amplitude.
+ for(unsigned period = 0; period < max_period_off; ++period, ++windows_passed_off)
+ {
+ for(unsigned i = 0; i < samples_per_interval;)
+ {
+ size_t blocksize = samples_per_interval - i;
+ blocksize = (blocksize < 256) ? blocksize : 256;
+ synth.generate(audioBuffer, blocksize);
+ for (unsigned j = 0; j < blocksize; ++j)
+ {
+ int16_t s = audioBuffer[2 * j];
+ audioHistory.add(s);
+ if(sound_min > s) sound_min = s;
+ if(sound_max < s) sound_max = s;
+ }
+ i += blocksize;
+ }
+
+ if(winsize != audioHistory.size())
+ {
+ winsize = audioHistory.size();
+ HannWindow(window.get(), winsize);
+ }
+
+ double rms = MeasureRMS(audioHistory.data(), window.get(), winsize);
+ /* ======== Find Key Off time ======== */
+ if(!keyoff_out_time_found && (rms <= peak_amplitude_value * min_coefficient_off))
+ {
+ keyoff_out_time = period;
+ keyoff_out_time_found = true;
+ }
+ /* ======== Find Key Off time ==END=== */
+ if(rms < highest_sofar * min_coefficient_off)
+ break;
+
+ if((period > max_silent * interval) && (sound_min >= -1 && sound_max <= 1))
+ break;
+ }
+
+ DurationInfo result;
+ result.peak_amplitude_time = peak_amplitude_time;
+ result.peak_amplitude_value = peak_amplitude_value;
+ result.begin_amplitude = begin_amplitude;
+ result.quarter_amplitude_time = (double)quarter_amplitude_time;
+ result.keyoff_out_time = (double)keyoff_out_time;
+
+ result.ms_sound_kon = (int64_t)(quarter_amplitude_time * 1000.0 / interval);
+ result.ms_sound_koff = (int64_t)(keyoff_out_time * 1000.0 / interval);
+ result.nosound = (peak_amplitude_value < 0.5) || ((sound_min >= -19) && (sound_max <= 18));
+
+ db.instruments[ins.instId].delay_on_ms = result.ms_sound_kon;
+ db.instruments[ins.instId].delay_off_ms = result.ms_sound_koff;
+ if(result.nosound)
+ db.instruments[ins.instId].instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_IsBlank;
+
+#ifdef GEN_ADLDATA_DEEP_DEBUG
+ /***************DEBUG******************/
+ ctx_wave_close(waveCtx);
+ /***************DEBUG******************/
+#endif
+ {
+ bool silent1 = result.nosound;
+ bool silent2 = BanksDump::isSilent(db, ins);
+ if(silent1 != silent2)
+ {
+ std::fprintf(stdout,
+ "\n\n%04lu - %s AN=%u NN=%u -- con1=%lu, con2=%lu\n%s computed - %s actual (%g peak, %d<%d)\n\n",
+ ins.instId, synth.m_isPseudo4op ? "pseudo4op" :
+ synth.m_isReal4op ? "4op" : "2op",
+ synth.m_actualNotesNum,
+ synth.m_notesNum,
+ (ins.fbConn) & 0x01,
+ (ins.fbConn >> 8) & 0x01,
+ silent2 ? "silent" : "sound",
+ silent1 ? "silent" : "sound",
+ peak_amplitude_value,
+ sound_min,
+ sound_max);
+ for(auto &sss : ins.instMetas)
+ std::fprintf(stdout, "%s\n", sss.c_str());
+ BanksDump::isSilent(db, ins, true);
+ std::fprintf(stdout, "\n\n");
+ std::fflush(stdout);
+// assert(silent1 == silent2);
+// exit(1);
+ }
+ }
+
+ return result;
+}
+
+
+MeasureThreaded::MeasureThreaded() :
+ m_semaphore(int(std::thread::hardware_concurrency()) * 2),
+ m_done(0),
+ m_cache_matches(0)
+{
+ DosBoxOPL3::globalPreInit();
+}
+
void MeasureThreaded::LoadCache(const char *fileName)
{
+ m_durationInfo.clear();
+
FILE *in = std::fopen(fileName, "rb");
if(!in)
{
@@ -448,14 +849,29 @@ void MeasureThreaded::LoadCache(const char *fileName)
if(std::fread(&inval, 1, sizeof(uint64_t), in) != sizeof(uint64_t))
break;
inst.insno2 = inval;
+ if(std::fread(&inst.instCache1.data, 1, 11, in) != 11)
+ break;
+ if(std::fread(&inst.instCache1.finetune, 1, 1, in) != 1)
+ break;
+ if(std::fread(&inst.instCache1.diff, 1, sizeof(bool), in) != sizeof(bool))
+ break;
+ if(std::fread(&inst.instCache2.data, 1, 11, in) != 11)
+ break;
+ if(std::fread(&inst.instCache2.finetune, 1, 1, in) != 1)
+ break;
+ if(std::fread(&inst.instCache2.diff, 1, sizeof(bool), in) != sizeof(bool))
+ break;
+
if(std::fread(&inst.notenum, 1, 1, in) != 1)
break;
if(std::fread(&inst.real4op, 1, 1, in) != 1)
break;
if(std::fread(&inst.pseudo4op, 1, 1, in) != 1)
break;
- if(std::fread(&inst.voice2_fine_tune, sizeof(double), 1, in) != 1)
+ int64_t voice2detune = 0;
+ if(std::fread(&voice2detune, sizeof(int64_t), 1, in) != 1)
break;
+ inst.voice2_fine_tune = static_cast<double>(voice2detune) / 1000000.0;
//Instrument data
if(fread(found_f, 1, 2 * sizeof(bool), in) != sizeof(bool) * 2)
@@ -534,18 +950,6 @@ void MeasureThreaded::LoadCache(const char *fileName)
}
//Duration data
- if(std::fread(&info.peak_amplitude_time, 1, sizeof(uint64_t), in) != sizeof(uint64_t))
- break;
- if(std::fread(&info.peak_amplitude_value, 1, sizeof(double), in) != sizeof(double))
- break;
- if(std::fread(&info.quarter_amplitude_time, 1, sizeof(double), in) != sizeof(double))
- break;
- if(std::fread(&info.begin_amplitude, 1, sizeof(double), in) != sizeof(double))
- break;
- if(std::fread(&info.interval, 1, sizeof(double), in) != sizeof(double))
- break;
- if(std::fread(&info.keyoff_out_time, 1, sizeof(double), in) != sizeof(double))
- break;
if(std::fread(&info.ms_sound_kon, 1, sizeof(int64_t), in) != sizeof(int64_t))
break;
if(std::fread(&info.ms_sound_koff, 1, sizeof(int64_t), in) != sizeof(int64_t))
@@ -579,10 +983,17 @@ void MeasureThreaded::SaveCache(const char *fileName)
fwrite(&outval, 1, sizeof(uint64_t), out);
outval = in.insno2;
fwrite(&outval, 1, sizeof(uint64_t), out);
+ fwrite(&in.instCache1.data, 1, 11, out);
+ fwrite(&in.instCache1.finetune, 1, 1, out);
+ fwrite(&in.instCache1.diff, 1, sizeof(bool), out);
+ fwrite(&in.instCache2.data, 1, 11, out);
+ fwrite(&in.instCache2.finetune, 1, 1, out);
+ fwrite(&in.instCache2.diff, 1, sizeof(bool), out);
fwrite(&in.notenum, 1, 1, out);
fwrite(&in.real4op, 1, 1, out);
fwrite(&in.pseudo4op, 1, 1, out);
- fwrite(&in.voice2_fine_tune, sizeof(double), 1, out);
+ int64_t voice2detune = static_cast<int64_t>(in.voice2_fine_tune * 1000000.0);
+ fwrite(&voice2detune, sizeof(int64_t), 1, out);
for(InstrumentDataTab::const_iterator j = insdatatab.begin(); j != insdatatab.end(); ++j)
{
@@ -608,12 +1019,6 @@ void MeasureThreaded::SaveCache(const char *fileName)
fwrite(&id[i].diff, 1, sizeof(bool), out);
}
- fwrite(&it->second.peak_amplitude_time, 1, sizeof(uint64_t), out);
- fwrite(&it->second.peak_amplitude_value, 1, sizeof(double), out);
- fwrite(&it->second.quarter_amplitude_time, 1, sizeof(double), out);
- fwrite(&it->second.begin_amplitude, 1, sizeof(double), out);
- fwrite(&it->second.interval, 1, sizeof(double), out);
- fwrite(&it->second.keyoff_out_time, 1, sizeof(double), out);
fwrite(&it->second.ms_sound_kon, 1, sizeof(int64_t), out);
fwrite(&it->second.ms_sound_koff, 1, sizeof(int64_t), out);
fwrite(&it->second.nosound, 1, sizeof(bool), out);
@@ -621,6 +1026,142 @@ void MeasureThreaded::SaveCache(const char *fileName)
std::fclose(out);
}
+void MeasureThreaded::LoadCacheX(const char *fileName)
+{
+ m_durationInfoX.clear();
+
+ FILE *in = std::fopen(fileName, "rb");
+ if(!in)
+ {
+ std::printf("Failed to load CacheX: file is not exists.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ char magic[32];
+ if(std::fread(magic, 1, 32, in) != 32)
+ {
+ std::fclose(in);
+ std::printf("Failed to load CacheX: can't read magic.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ if(std::memcmp(magic, "ADLMIDI-DURATION-CACHE-FILE-V2.0", 32) != 0)
+ {
+ std::fclose(in);
+ std::printf("Failed to load CacheX: magic missmatch.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ uint_fast32_t itemsCount;
+ uint8_t itemsCountA[4];
+ if(std::fread(itemsCountA, 1, 4, in) != 4)
+ {
+ std::fclose(in);
+ std::printf("Failed to load CacheX: can't read cache size value.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ itemsCount = static_cast<uint_fast32_t>(toUint32LE(itemsCountA));
+
+ while(!std::feof(in) && itemsCount > 0)
+ {
+ OperatorsKey k;
+ DurationInfo v;
+
+ uint8_t data_k[5];
+
+ for(auto &kv : k)
+ {
+ uint8_t data[4];
+ auto ret = std::fread(data, 1, 4, in);
+ if(ret != 4)
+ {
+ std::fclose(in);
+ std::printf("Failed to load CacheX: unexpected end of file.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+ kv = static_cast<int_fast32_t>(toSint32LE(data));
+ }
+
+ auto ret = std::fread(data_k, 1, 5, in);
+ if(ret != 5)
+ {
+ std::fclose(in);
+ std::printf("Failed to load CacheX: unexpected end of file.\n"
+ "Complete data will be generated from scratch.\n");
+ std::fflush(stdout);
+ return;
+ }
+
+ v.ms_sound_kon = static_cast<int_fast64_t>(toUint16LE(data_k + 0));
+ v.ms_sound_koff = static_cast<int_fast64_t>(toUint16LE(data_k + 2));
+ v.nosound = (data_k[4] == 0x01);
+
+ m_durationInfoX.insert({k, v});
+ itemsCount--;
+ }
+
+ std::printf("CacheX loaded!\n");
+ std::fflush(stdout);
+
+ std::fclose(in);
+}
+
+void MeasureThreaded::SaveCacheX(const char *fileName)
+{
+ FILE *out = std::fopen(fileName, "wb");
+ std::fprintf(out, "ADLMIDI-DURATION-CACHE-FILE-V2.0");
+
+ uint_fast32_t itemsCount = static_cast<uint_fast32_t>(m_durationInfoX.size());
+ uint8_t itemsCountA[4] =
+ {
+ static_cast<uint8_t>((itemsCount >> 0) & 0xFF),
+ static_cast<uint8_t>((itemsCount >> 8) & 0xFF),
+ static_cast<uint8_t>((itemsCount >> 16) & 0xFF),
+ static_cast<uint8_t>((itemsCount >> 24) & 0xFF)
+ };
+ std::fwrite(itemsCountA, 1, 4, out);
+
+ for(DurationInfoCacheX::iterator it = m_durationInfoX.begin(); it != m_durationInfoX.end(); it++)
+ {
+ const OperatorsKey &k = it->first;
+ const DurationInfo &v = it->second;
+
+ uint8_t data_k[5] =
+ {
+ static_cast<uint8_t>((v.ms_sound_kon >> 0) & 0xFF),
+ static_cast<uint8_t>((v.ms_sound_kon >> 8) & 0xFF),
+ static_cast<uint8_t>((v.ms_sound_koff >> 0) & 0xFF),
+ static_cast<uint8_t>((v.ms_sound_koff >> 8) & 0xFF),
+ static_cast<uint8_t>(v.nosound ? 0x01 : 0x00)
+ };
+
+ for(auto &kv : k)
+ {
+ uint8_t data[4] =
+ {
+ static_cast<uint8_t>((kv >> 0) & 0xFF),
+ static_cast<uint8_t>((kv >> 8) & 0xFF),
+ static_cast<uint8_t>((kv >> 16) & 0xFF),
+ static_cast<uint8_t>((kv >> 24) & 0xFF)
+ };
+ std::fwrite(data, 1, 4, out);
+ }
+ std::fwrite(data_k, 1, 5, out);
+ }
+ std::fclose(out);
+}
+
#ifdef ADL_GENDATA_PRINT_PROGRESS
static const char* spinner = "-\\|/";
@@ -671,6 +1212,36 @@ void MeasureThreaded::run(InstrumentsData::const_iterator i)
destData *dd = new destData;
dd->i = i;
+ dd->bd = nullptr;
+ dd->bd_ins = nullptr;
+ dd->myself = this;
+ dd->start();
+ m_threads.push_back(dd);
+#ifdef ADL_GENDATA_PRINT_PROGRESS
+ printProgress();
+#endif
+}
+
+void MeasureThreaded::run(BanksDump &bd, BanksDump::InstrumentEntry &e)
+{
+ m_semaphore.wait();
+ if(m_threads.size() > 0)
+ {
+ for(std::vector<destData *>::iterator it = m_threads.begin(); it != m_threads.end();)
+ {
+ if(!(*it)->m_works)
+ {
+ delete(*it);
+ it = m_threads.erase(it);
+ }
+ else
+ it++;
+ }
+ }
+
+ destData *dd = new destData;
+ dd->bd = &bd;
+ dd->bd_ins = &e;
dd->myself = this;
dd->start();
m_threads.push_back(dd);
@@ -702,18 +1273,55 @@ void MeasureThreaded::destData::callback(void *myself)
destData *s = reinterpret_cast<destData *>(myself);
DurationInfo info;
DosBoxOPL3 dosbox;
- DurationInfoCache::iterator cachedEntry = s->myself->m_durationInfo.find(s->i->first);
+ // NukedOPL3 dosbox;
- if(cachedEntry != s->myself->m_durationInfo.end())
+ if(s->bd)
{
- s->myself->m_cache_matches++;
- goto endWork;
+ OperatorsKey ok = {s->bd_ins->ops[0], s->bd_ins->ops[1], s->bd_ins->ops[2], s->bd_ins->ops[3],
+ static_cast<int_fast32_t>(s->bd_ins->fbConn),
+ s->bd_ins->noteOffset1, s->bd_ins->noteOffset2,
+ static_cast<int_fast32_t>(s->bd_ins->percussionKeyNumber),
+ static_cast<int_fast32_t>(s->bd_ins->instFlags),
+ static_cast<int_fast32_t>(s->bd_ins->secondVoiceDetune)};
+ s->myself->m_durationInfo_mx.lock();
+ DurationInfoCacheX::iterator cachedEntry = s->myself->m_durationInfoX.find(ok);
+ bool atEnd = cachedEntry == s->myself->m_durationInfoX.end();
+ s->myself->m_durationInfo_mx.unlock();
+
+ if(!atEnd)
+ {
+ const DurationInfo &di = cachedEntry->second;
+ s->bd_ins->delay_on_ms = di.ms_sound_kon;
+ s->bd_ins->delay_off_ms = di.ms_sound_koff;
+ if(di.nosound)
+ s->bd_ins->instFlags |= BanksDump::InstrumentEntry::WOPL_Ins_IsBlank;
+ s->myself->m_cache_matches++;
+ goto endWork;
+ }
+ info = MeasureDurations(*s->bd, *s->bd_ins, &dosbox);
+ s->myself->m_durationInfo_mx.lock();
+ s->myself->m_durationInfoX.insert({ok, info});
+ s->myself->m_durationInfo_mx.unlock();
}
+ else
+ {
+ const ins &ok = s->i->first;
+ s->myself->m_durationInfo_mx.lock();
+ DurationInfoCache::iterator cachedEntry = s->myself->m_durationInfo.find(ok);
+ bool atEnd = cachedEntry == s->myself->m_durationInfo.end();
+ s->myself->m_durationInfo_mx.unlock();
- info = MeasureDurations(s->i->first, &dosbox);
- s->myself->m_durationInfo_mx.lock();
- s->myself->m_durationInfo.insert({s->i->first, info});
- s->myself->m_durationInfo_mx.unlock();
+ if(!atEnd)
+ {
+ s->myself->m_cache_matches++;
+ goto endWork;
+ }
+
+ info = MeasureDurations(ok, &dosbox);
+ s->myself->m_durationInfo_mx.lock();
+ s->myself->m_durationInfo.insert({ok, info});
+ s->myself->m_durationInfo_mx.unlock();
+ }
endWork:
s->myself->m_semaphore.notify();