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
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
* Copyright (C) 1999 - 2005 Simon Peter <dn.tlp@gmx.net>, et al.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* herad.h - Herbulot AdLib Player by Stas'M <binarymaster@mail.ru>
*
* Thanks goes to co-workers:
* -=CHE@TER=- (SQX decompression)
* SynaMax (general documentation, reverse-engineering, testing)
* Jepael (timer code sample, DOS driver shell)
* Daniël van de Burgt "thatdutchguy" (pitch slides code sample)
*
* REFERENCES:
* http://www.vgmpf.com/Wiki/index.php/HERAD
*/
#ifndef H_ADPLUG_HERADPLAYER
#define H_ADPLUG_HERADPLAYER
#include <stdint.h>
#include "player.h"
#define HERAD_MIN_SIZE 6 /* Minimum file size for compression detection */
#define HERAD_MAX_SIZE 75775 /* Maximum possible file size: 0xFFFF + 256 * HERAD_INST_SIZE */
#define HERAD_HEAD_SIZE 52 /* File header size */
#define HERAD_COMP_NONE 0
#define HERAD_COMP_HSQ 1
#define HERAD_COMP_SQX 2
#define HERAD_MAX_TRACKS 21
#define HERAD_INST_SIZE 40
#define HERAD_INSTMODE_SDB1 0 /* HERAD SDB version 1 instrument */
#define HERAD_INSTMODE_SDB2 1 /* HERAD SDB version 2 instrument */
#define HERAD_INSTMODE_AGD 4 /* HERAD AGD instrument */
#define HERAD_INSTMODE_KMAP -1 /* HERAD version 2 keymap */
#define HERAD_FNUM_MIN 325 /* Minimum note frequency number */
#define HERAD_FNUM_MAX 685 /* Maximum note frequency number */
#define HERAD_BEND_CENTER 0x40 /* Pitch bend middle value */
#define HERAD_NOTE_OFF 0
#define HERAD_NOTE_ON 1
#define HERAD_NOTE_UPDATE 2
#define HERAD_NUM_VOICES 9
#define HERAD_NUM_NOTES 12
#define HERAD_MEASURE_TICKS 96
//#define HERAD_USE_LOOPING /* Uncomment this to enable looping */
class CheradPlayer: public CPlayer
{
public:
static CPlayer *factory(Copl *newopl);
CheradPlayer(Copl *newopl)
: CPlayer(newopl), track(0), chn(0), inst(0)
{ }
~CheradPlayer()
{
if (track)
{
for (int i = 0; i < nTracks; i++)
{
if (track[i].data) delete[] track[i].data;
}
delete[] track;
}
if (chn) delete[] chn;
if (inst) delete[] inst;
};
bool load(const std::string &filename, const CFileProvider &fp);
bool update();
void rewind(int subsong);
float getrefresh()
{
return (float)200.299;
};
unsigned int getspeed()
{
return wSpeed;
};
unsigned int getpatterns()
{
return total_ticks / HERAD_MEASURE_TICKS + (total_ticks % HERAD_MEASURE_TICKS ? 1 : 0);
};
unsigned int getpattern()
{
return (ticks_pos <= 0 ? 0: (ticks_pos - 1) / HERAD_MEASURE_TICKS + 1);
};
unsigned int getrow()
{
return (ticks_pos <= 0 ? 0 : (ticks_pos - 1) % HERAD_MEASURE_TICKS);
};
std::string gettype();
unsigned int getinstruments()
{
return inst ? nInsts : 0;
};
std::string getinstrument(unsigned int n)
{
return std::string();
};
private:
uint32_t GetTicks(uint8_t t);
void executeCommand(uint8_t t);
void processEvents();
void ev_noteOn(uint8_t ch, uint8_t note, uint8_t vel);
void ev_noteOff(uint8_t ch, uint8_t note, uint8_t vel);
void ev_programChange(uint8_t ch, uint8_t prog);
void ev_aftertouch(uint8_t ch, uint8_t vel);
void ev_pitchBend(uint8_t ch, uint8_t bend);
void playNote(uint8_t c, uint8_t note, uint8_t state);
void setFreq(uint8_t c, uint8_t oct, uint16_t freq, bool on);
void changeProgram(uint8_t c, uint8_t i);
void macroModOutput(uint8_t c, uint8_t i, int8_t sens, uint8_t level);
void macroCarOutput(uint8_t c, uint8_t i, int8_t sens, uint8_t level);
void macroFeedback(uint8_t c, uint8_t i, int8_t sens, uint8_t level);
void macroTranspose(uint8_t * note, uint8_t i);
void macroSlide(uint8_t c);
static const uint8_t slot_offset[HERAD_NUM_VOICES];
static const uint16_t FNum[HERAD_NUM_NOTES];
static const uint8_t fine_bend[HERAD_NUM_NOTES + 1];
static const uint8_t coarse_bend[10];
protected:
bool songend;
int16_t wTime;
int32_t ticks_pos; /* current tick counter */
uint32_t total_ticks; /* total ticks in song */
uint8_t comp; /* File compression type (see HERAD_COMP_*) */
bool AGD; /* Whether this is HERAD AGD (OPL3) */
bool v2; /* Whether this is HERAD version 2 */
uint8_t nTracks; /* Number of tracks */
uint8_t nInsts; /* Number of instruments */
uint16_t wLoopStart; /* Loop starts at this measure (0 = don't loop) */
uint16_t wLoopEnd; /* Loop ends at this measure (0 = don't loop) */
uint16_t wLoopCount; /* Number of times the selected measures will play (0 = loop forever; >0 - play N times) */
uint16_t wSpeed; /* Fixed point value that controls music speed. Value range is 0x0100 - 0x8100 */
struct herad_trk {
// stored variables
uint16_t size; /* data size */
uint8_t * data; /* event data */
// variables for playback
uint16_t pos; /* data position */
uint32_t counter; /* tick counter */
uint16_t ticks; /* ticks to wait for next event */
};
struct herad_chn {
uint8_t program; /* current instrument */
uint8_t playprog; /* current playing instrument (used for keymap) */
uint8_t note; /* current note */
bool keyon; /* note is active */
uint8_t bend; /* current pitch bend */
uint8_t slide_dur; /* pitch slide duration */
};
struct herad_inst_data {
int8_t mode; /* instrument mode (see HERAD_INSTMODE_*) */
uint8_t voice; /* voice number, unused */
uint8_t mod_ksl; /* modulator: KSL */
uint8_t mod_mul; /* modulator: frequency multiplier */
uint8_t feedback; /* feedback */
uint8_t mod_A; /* modulator: attack */
uint8_t mod_S; /* modulator: sustain */
uint8_t mod_eg; /* modulator: envelope gain */
uint8_t mod_D; /* modulator: decay */
uint8_t mod_R; /* modulator: release */
uint8_t mod_out; /* modulator: output level */
uint8_t mod_am; /* modulator: amplitude modulation (tremolo) */
uint8_t mod_vib; /* modulator: frequency vibrato */
uint8_t mod_ksr; /* modulator: key scaling/envelope rate */
uint8_t con; /* connector */
uint8_t car_ksl; /* carrier: KSL */
uint8_t car_mul; /* carrier: frequency multiplier */
uint8_t pan; /* panning */
uint8_t car_A; /* carrier: attack */
uint8_t car_S; /* carrier: sustain */
uint8_t car_eg; /* carrier: envelope gain */
uint8_t car_D; /* carrier: decay */
uint8_t car_R; /* carrier: release */
uint8_t car_out; /* carrier: output level */
uint8_t car_am; /* carrier: amplitude modulation (tremolo) */
uint8_t car_vib; /* carrier: frequency vibrato */
uint8_t car_ksr; /* carrier: key scaling/envelope rate */
int8_t mc_fb_at; /* macro: modify feedback with aftertouch */
uint8_t mod_wave; /* modulator: waveform select */
uint8_t car_wave; /* carrier: waveform select */
int8_t mc_mod_out_vel; /* macro: modify modulator output level with note velocity */
int8_t mc_car_out_vel; /* macro: modify carrier output level with note velocity */
int8_t mc_fb_vel; /* macro: modify feedback with note velocity */
uint8_t mc_slide_coarse; /* macro: pitch slide coarse tune */
uint8_t mc_transpose; /* macro: root note transpose */
uint8_t mc_slide_dur; /* macro: pitch slide duration (in ticks) */
int8_t mc_slide_range; /* macro: pitch slide range */
uint8_t dummy; /* unknown value */
int8_t mc_mod_out_at; /* macro: modify modulator output level with aftertouch */
int8_t mc_car_out_at; /* macro: modify carrier output level with aftertouch */
};
struct herad_keymap {
int8_t mode; /* same as herad_inst_data */
uint8_t voice; /* same as herad_inst_data */
uint8_t offset; /* keymap start note: 0 => C2 (24), 24 => C4 (48) */
uint8_t dummy; /* unknown value */
uint8_t index[HERAD_INST_SIZE-4]; /* array of instruments */
};
union herad_inst {
uint8_t data[HERAD_INST_SIZE]; /* array of 8-bit parameters */
herad_inst_data param; /* structure of parameters */
herad_keymap keymap; /* keymap structure */
};
herad_trk * track; /* event tracks [nTracks] */
herad_chn * chn; /* active channels [nTracks] */
herad_inst * inst; /* instruments [nInsts] */
int32_t loop_pos;
uint16_t loop_times;
herad_trk loop_data[HERAD_MAX_TRACKS];
};
#endif