1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
|
/*
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADLMIDI_OPL3_HPP
#define ADLMIDI_OPL3_HPP
#include "oplinst.h"
#include "adlmidi_ptr.hpp"
#include "adlmidi_private.hpp"
#include "adlmidi_bankmap.h"
#define BEND_COEFFICIENT 172.4387
#define OPL3_CHANNELS_MELODIC_BASE 0
#define OPL3_CHANNELS_RHYTHM_BASE 18
#define NUM_OF_CHANNELS 23
#define NUM_OF_4OP_CHANNELS 6
#define NUM_OF_2OP_CHANNELS 18
#define NUM_OF_2x2_CHANNELS 9
#define NUM_OF_OPL2_CHANNELS 9
#define NUM_OF_RM_CHANNELS 5
/**
* @brief OPL3 Chip management class
*/
class OPL3
{
friend class MIDIplay;
friend class AdlInstrumentTester;
friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent);
public:
enum
{
PercussionTag = 1 << 15,
CustomBankTag = 0xFFFFFFFF
};
//! Total number of chip channels between all running emulators
uint32_t m_numChannels;
//! Just a padding. Reserved.
char _padding[4];
//! Running chip emulators
std::vector<AdlMIDI_SPtr<OPLChipBase > > m_chips;
private:
//! Cached patch data, needed by Touch()
std::vector<OplTimbre> m_insCache;
//! Value written to B0, cached, needed by NoteOff.
/*! Contains Key on/off state, octave block and frequency number values
*/
std::vector<uint32_t> m_keyBlockFNumCache;
//! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode)
std::vector<uint32_t> m_regBD;
//! Cached C0 register value (primarily for the panning state)
std::vector<uint8_t> m_regC0;
#ifdef ADLMIDI_ENABLE_HW_SERIAL
bool m_serial;
std::string m_serialName;
unsigned m_serialBaud;
unsigned m_serialProtocol;
#endif
//! Does loaded emulator supports soft panning?
bool m_softPanningSup;
//! Current type of chip
int m_currentChipType;
//! Number channels per chip
size_t m_perChipChannels;
/*!
* \brief Current state of the synth (if values matched to setup, chips and arrays won't be fully re-created)
*/
struct State
{
int emulator;
uint32_t numChips;
unsigned long pcm_rate;
State()
{
clear();
}
void clear()
{
emulator = -2;
numChips = 0;
pcm_rate = 0;
}
bool cmp_rate(unsigned long rate)
{
bool ret = pcm_rate != rate;
if(ret)
pcm_rate = rate;
return ret;
}
bool cmp(int emu, uint32_t chips)
{
bool ret = emu != emulator || chips != numChips;
if(ret)
{
emulator = emu;
numChips = chips;
}
return ret;
}
} m_curState;
public:
/**
* @brief MIDI bank entry
*/
struct Bank
{
//! MIDI Bank instruments
OplInstMeta ins[128];
};
typedef BasicBankMap<Bank> BankMap;
//! MIDI bank instruments data
BankMap m_insBanks;
//! MIDI bank-wide setup
OplBankSetup m_insBankSetup;
public:
//! Blank instrument template
static const OplInstMeta m_emptyInstrument;
//! Total number of running concurrent emulated chips
uint32_t m_numChips;
//! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank.
uint32_t m_embeddedBank;
//! Total number of needed four-operator channels in all running chips
uint32_t m_numFourOps;
//! Turn global Deep Tremolo mode on
bool m_deepTremoloMode;
//! Turn global Deep Vibrato mode on
bool m_deepVibratoMode;
//! Use Rhythm Mode percussions
bool m_rhythmMode;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool m_scaleModulators;
//! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
bool m_runAtPcmRate;
//! Enable soft panning
bool m_softPanning;
//! Master volume, controlled via SysEx (0...127)
uint8_t m_masterVolume;
//! Just a padding. Reserved.
char _padding2[3];
/**
* @brief Music playing mode
*/
enum MusicMode
{
//! MIDI mode
MODE_MIDI,
//! AIL XMIDI mode
MODE_XMIDI,
//! Id-Software Music mode
MODE_IMF,
//! Creative Music Files mode
MODE_CMF,
//! EA-MUS (a.k.a. RSXX) mode
MODE_RSXX
} m_musicMode;
/**
* @brief Volume models enum
*/
enum VolumesScale
{
//! Generic volume model (linearization of logarithmic scale)
VOLUME_Generic,
//! OPL3 native logarithmic scale
VOLUME_NATIVE,
//! DMX volume scale logarithmic table
VOLUME_DMX,
//! Apoge Sound System volume scaling model
VOLUME_APOGEE,
//! Windows 9x SB16 driver volume scale table
VOLUME_9X,
//! DMX model with a fixed bug of AM voices
VOLUME_DMX_FIXED,
//! Apogee model with a fixed bug of AM voices
VOLUME_APOGEE_FIXED,
//! Audio Interfaces Library volume scaling model
VOLUME_AIL,
//! Windows 9x Generic FM driver volume scale table
VOLUME_9X_GENERIC_FM,
//! HMI Sound Operating System volume scale table
VOLUME_HMI,
//! HMI Sound Operating System volume scale model, older variant
VOLUME_HMI_OLD
} m_volumeScale;
//! Channel allocation algorithm
ADLMIDI_ChannelAlloc m_channelAlloc;
//! Reserved
char _padding3[8];
/**
* @brief Channel categiry enumeration
*/
enum ChanCat
{
//! Regular melodic/percussion channel
ChanCat_Regular = 0,
//! Four-op first part
ChanCat_4op_First = 1,
//! Four-op second part
ChanCat_4op_Second = 2,
//! Rhythm-mode Bass drum
ChanCat_Rhythm_Bass = 3,
//! Rhythm-mode Snare drum
ChanCat_Rhythm_Snare = 4,
//! Rhythm-mode Tom-Tom
ChanCat_Rhythm_Tom = 5,
//! Rhythm-mode Cymbal
ChanCat_Rhythm_Cymbal = 6,
//! Rhythm-mode Hi-Hat
ChanCat_Rhythm_HiHat = 7,
//! Rhythm-mode Secondary channel
ChanCat_Rhythm_Secondary = 8,
//! Here is no channel used (OPL2 only)
ChanCat_None = 9
};
//! Category of the channel
/*! 1 = quad-first, 2 = quad-second, 0 = regular
3 = percussion BassDrum
4 = percussion Snare
5 = percussion Tom
6 = percussion Crash cymbal
7 = percussion Hihat
8 = percussion Secondary
*/
std::vector<uint32_t> m_channelCategory;
/**
* @brief C.O. Constructor
*/
OPL3();
/**
* @brief C.O. Destructor
*/
~OPL3();
/**
* @brief Checks are setup locked to be changed on the fly or not
* @return true when setup on the fly is locked
*/
bool setupLocked();
/**
* @brief Choose one of embedded banks
* @param bank ID of the bank
*/
void setEmbeddedBank(uint32_t bank);
/**
* @brief Write data to OPL3 chip register
* @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
* @param address Register address to write
* @param value Value to write
*/
void writeReg(size_t chip, uint16_t address, uint8_t value);
/**
* @brief Write data to OPL3 chip register
* @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored
* @param address Register address to write
* @param value Value to write
*/
void writeRegI(size_t chip, uint32_t address, uint32_t value);
/**
* @brief Write to soft panning control of OPL3 chip emulator
* @param chip Index of emulated chip.
* @param address Register of channel to write
* @param value Value to write
*/
void writePan(size_t chip, uint32_t address, uint32_t value);
/**
* @brief Off the note in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
*/
void noteOff(size_t c);
/**
* @brief On the note in specified chip channel with specified frequency of the tone
* @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param c2 Second 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param tone The tone to play (integer part - MIDI halftone, decimal part - relative bend offset)
*/
void noteOn(size_t c1, size_t c2, double tone);
/**
* @brief Change setup of instrument in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param velocity Note velocity (from 0 to 127)
* @param channelVolume Channel volume level (from 0 to 127)
* @param channelExpression Channel expression level (from 0 to 127)
* @param brightness CC74 Brightness level (from 0 to 127)
* @param isDrum Is this a drum note? This flag is needed for some volume model algorithms
*/
void touchNote(size_t c,
uint_fast32_t velocity,
uint_fast32_t channelVolume = 127,
uint_fast32_t channelExpression = 127,
uint_fast32_t brightness = 127,
bool isDrum = false);
/**
* @brief Set the instrument into specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param instrument Instrument data to set into the chip channel
*/
void setPatch(size_t c, const OplTimbre &instrument);
/**
* @brief Set panpot position
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param value 3-bit panpot value
*/
void setPan(size_t c, uint8_t value);
/**
* @brief Shut up all chip channels
*/
void silenceAll();
/**
* @brief Commit updated flag states to chip registers
*/
void updateChannelCategories();
/**
* @brief commit deepTremolo and deepVibrato flags
*/
void commitDeepFlags();
/**
* @brief Set the volume scaling model
* @param volumeModel Type of volume scale model scale
*/
void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel);
/**
* @brief Get the volume scaling model
*/
ADLMIDI_VolumeModels getVolumeScaleModel();
/**
* @brief Clean up all running emulated chip instances
*/
void clearChips();
/**
* @brief Reset chip properties and initialize them
* @param emulator Type of chip emulator
* @param PCM_RATE Output sample rate to generate on output
* @param audioTickHandler PCM-accurate clock hook
*/
void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler);
void initChip(size_t chip);
#ifdef ADLMIDI_ENABLE_HW_SERIAL
/**
* @brief Reset chip properties for hardware use
* @param emulator
* @param PCM_RATE
* @param audioTickHandler
*/
void resetSerial(const std::string &serialName, unsigned int baud, unsigned int protocol);
#endif
};
/**
* @brief Check emulator availability
* @param emulator Emulator ID (ADL_Emulator)
* @return true when emulator is available
*/
extern bool adl_isEmulatorAvailable(int emulator);
/**
* @brief Find highest emulator
* @return The ADL_Emulator enum value which contains ID of highest emulator
*/
extern int adl_getHighestEmulator();
/**
* @brief Find lowest emulator
* @return The ADL_Emulator enum value which contains ID of lowest emulator
*/
extern int adl_getLowestEmulator();
#endif // ADLMIDI_OPL3_HPP
|