|
359
|
1 /*
|
|
|
2 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
|
|
|
3 * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
|
|
|
4 *
|
|
|
5 * This library is free software; you can redistribute it and/or
|
|
|
6 * modify it under the terms of the GNU Lesser General Public
|
|
|
7 * License as published by the Free Software Foundation; either
|
|
|
8 * version 2.1 of the License, or (at your option) any later version.
|
|
|
9 *
|
|
|
10 * This library is distributed in the hope that it will be useful,
|
|
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
13 * Lesser General Public License for more details.
|
|
|
14 *
|
|
|
15 * You should have received a copy of the GNU Lesser General Public
|
|
|
16 * License along with this library; if not, write to the Free Software
|
|
|
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
18 *
|
|
|
19 * rol.h - ROL Player by OPLx <oplx@yahoo.com>
|
|
|
20 *
|
|
|
21 * Visit: http://tenacity.hispeed.com/aomit/oplx/
|
|
|
22 */
|
|
|
23 #include <algorithm>
|
|
|
24
|
|
|
25 #include "rol.h"
|
|
|
26 #include "debug.h"
|
|
|
27
|
|
|
28 int const CrolPlayer::kSizeofDataRecord = 30;
|
|
|
29 int const CrolPlayer::kMaxTickBeat = 60;
|
|
|
30 int const CrolPlayer::kSilenceNote = -12;
|
|
|
31 int const CrolPlayer::kNumMelodicVoices = 9;
|
|
|
32 int const CrolPlayer::kNumPercussiveVoices = 11;
|
|
|
33 int const CrolPlayer::kBassDrumChannel = 6;
|
|
|
34 int const CrolPlayer::kSnareDrumChannel = 7;
|
|
|
35 int const CrolPlayer::kTomtomChannel = 8;
|
|
|
36 int const CrolPlayer::kTomtomFreq = 2;//4;
|
|
|
37 int const CrolPlayer::kSnareDrumFreq = 2;//kTomtomFreq + 7;
|
|
|
38 float const CrolPlayer::kDefaultUpdateTme = 18.2f;
|
|
|
39 float const CrolPlayer::kPitchFactor = 400.0f;
|
|
|
40
|
|
|
41 static const unsigned char drum_table[4] = {0x14, 0x12, 0x15, 0x11};
|
|
|
42
|
|
|
43 CrolPlayer::uint16 const CrolPlayer::kNoteTable[12] =
|
|
|
44 {
|
|
|
45 340, // C
|
|
|
46 363, // C#
|
|
|
47 385, // D
|
|
|
48 408, // D#
|
|
|
49 432, // E
|
|
|
50 458, // F
|
|
|
51 485, // F#
|
|
|
52 514, // G
|
|
|
53 544, // G#
|
|
|
54 577, // A
|
|
|
55 611, // A#
|
|
|
56 647 // B
|
|
|
57 };
|
|
|
58
|
|
|
59 /*** public methods **************************************/
|
|
|
60
|
|
|
61 CPlayer *CrolPlayer::factory(Copl *newopl)
|
|
|
62 {
|
|
|
63 return new CrolPlayer(newopl);
|
|
|
64 }
|
|
|
65 //---------------------------------------------------------
|
|
|
66 CrolPlayer::CrolPlayer(Copl *newopl)
|
|
|
67 : CPlayer ( newopl )
|
|
|
68 ,rol_header ( NULL )
|
|
|
69 ,mNextTempoEvent ( 0 )
|
|
|
70 ,mCurrTick ( 0 )
|
|
|
71 ,mTimeOfLastNote ( 0 )
|
|
|
72 ,mRefresh ( kDefaultUpdateTme )
|
|
|
73 ,bdRegister ( 0 )
|
|
|
74 {
|
|
|
75 int n;
|
|
|
76
|
|
|
77 memset(bxRegister, 0, sizeof(bxRegister) );
|
|
|
78 memset(volumeCache, 0, sizeof(volumeCache) );
|
|
|
79 memset(freqCache, 0, sizeof(freqCache) );
|
|
|
80
|
|
|
81 for(n=0; n<11; n++)
|
|
|
82 pitchCache[n]=1.0f;
|
|
|
83 }
|
|
|
84 //---------------------------------------------------------
|
|
|
85 CrolPlayer::~CrolPlayer()
|
|
|
86 {
|
|
|
87 if( rol_header != NULL )
|
|
|
88 {
|
|
|
89 delete rol_header;
|
|
|
90 rol_header=NULL;
|
|
|
91 }
|
|
|
92 }
|
|
|
93 //---------------------------------------------------------
|
|
|
94 bool CrolPlayer::load(const std::string &filename, const CFileProvider &fp)
|
|
|
95 {
|
|
|
96 binistream *f = fp.open(filename); if(!f) return false;
|
|
|
97
|
|
|
98 char *fn = new char[filename.length()+9];
|
|
|
99 int i;
|
|
|
100 std::string bnk_filename;
|
|
|
101
|
|
|
102 AdPlug_LogWrite("*** CrolPlayer::load(f, \"%s\") ***\n", filename.c_str());
|
|
|
103 strcpy(fn,filename.data());
|
|
|
104 for (i=strlen(fn)-1; i>=0; i--)
|
|
|
105 if (fn[i] == '/' || fn[i] == '\\')
|
|
|
106 break;
|
|
|
107 strcpy(fn+i+1,"standard.bnk");
|
|
|
108 bnk_filename = fn;
|
|
|
109 delete [] fn;
|
|
|
110 AdPlug_LogWrite("bnk_filename = \"%s\"\n",bnk_filename.c_str());
|
|
|
111
|
|
|
112 rol_header = new SRolHeader;
|
|
|
113 memset( rol_header, 0, sizeof(SRolHeader) );
|
|
|
114
|
|
|
115 rol_header->version_major = f->readInt( 2 );
|
|
|
116 rol_header->version_minor = f->readInt( 2 );
|
|
|
117
|
|
|
118 // Version check
|
|
|
119 if(rol_header->version_major != 0 || rol_header->version_minor != 4) {
|
|
|
120 AdPlug_LogWrite("Unsupported file version %d.%d or not a ROL file!\n",
|
|
|
121 rol_header->version_major, rol_header->version_minor);
|
|
|
122 AdPlug_LogWrite("--- CrolPlayer::load ---\n");
|
|
|
123 fp.close(f);
|
|
|
124 return false;
|
|
|
125 }
|
|
|
126
|
|
|
127 f->seek( 40, binio::Add );
|
|
|
128
|
|
|
129 rol_header->ticks_per_beat = f->readInt( 2 );
|
|
|
130 rol_header->beats_per_measure = f->readInt( 2 );
|
|
|
131 rol_header->edit_scale_y = f->readInt( 2 );
|
|
|
132 rol_header->edit_scale_x = f->readInt( 2 );
|
|
|
133
|
|
|
134 f->seek( 1, binio::Add );
|
|
|
135
|
|
|
136 rol_header->mode = f->readInt(1);
|
|
|
137
|
|
|
138 f->seek( 90+38+15, binio::Add );
|
|
|
139
|
|
|
140 rol_header->basic_tempo = f->readFloat( binio::Single );
|
|
|
141
|
|
|
142 load_tempo_events( f );
|
|
|
143
|
|
|
144 mTimeOfLastNote = 0;
|
|
|
145
|
|
|
146 if( load_voice_data( f, bnk_filename, fp ) != true )
|
|
|
147 {
|
|
|
148 AdPlug_LogWrite("CrolPlayer::load_voice_data(f) failed!\n");
|
|
|
149 AdPlug_LogWrite("--- CrolPlayer::load ---\n");
|
|
|
150
|
|
|
151 fp.close( f );
|
|
|
152 return false;
|
|
|
153 }
|
|
|
154
|
|
|
155 fp.close( f );
|
|
|
156
|
|
|
157 rewind( 0 );
|
|
|
158 AdPlug_LogWrite("--- CrolPlayer::load ---\n");
|
|
|
159 return true;
|
|
|
160 }
|
|
|
161 //---------------------------------------------------------
|
|
|
162 bool CrolPlayer::update()
|
|
|
163 {
|
|
|
164 if( mNextTempoEvent < mTempoEvents.size() &&
|
|
|
165 mTempoEvents[mNextTempoEvent].time == mCurrTick )
|
|
|
166 {
|
|
|
167 SetRefresh( mTempoEvents[mNextTempoEvent].multiplier );
|
|
|
168 ++mNextTempoEvent;
|
|
|
169 }
|
|
|
170
|
|
|
171 TVoiceData::iterator curr = voice_data.begin();
|
|
|
172 TVoiceData::iterator end = voice_data.end();
|
|
|
173 int voice = 0;
|
|
|
174
|
|
|
175 while( curr != end )
|
|
|
176 {
|
|
|
177 UpdateVoice( voice, *curr );
|
|
|
178 ++curr;
|
|
|
179 ++voice;
|
|
|
180 }
|
|
|
181
|
|
|
182 ++mCurrTick;
|
|
|
183
|
|
|
184 if( mCurrTick > mTimeOfLastNote )
|
|
|
185 {
|
|
|
186 return false;
|
|
|
187 }
|
|
|
188
|
|
|
189 return true;
|
|
|
190 //return ( mCurrTick > mTimeOfLastNote ) ? false : true;
|
|
|
191 }
|
|
|
192 //---------------------------------------------------------
|
|
|
193 void CrolPlayer::rewind( int subsong )
|
|
|
194 {
|
|
|
195 TVoiceData::iterator curr = voice_data.begin();
|
|
|
196 TVoiceData::iterator end = voice_data.end();
|
|
|
197
|
|
|
198 while( curr != end )
|
|
|
199 {
|
|
|
200 CVoiceData &voice = *curr;
|
|
|
201
|
|
|
202 voice.Reset();
|
|
|
203 ++curr;
|
|
|
204 }
|
|
|
205
|
|
|
206 memset(bxRegister, 0, sizeof(bxRegister) );
|
|
|
207 memset(volumeCache, 0, sizeof(volumeCache) );
|
|
|
208
|
|
|
209 bdRegister = 0;
|
|
|
210
|
|
|
211 opl->init(); // initialize to melodic by default
|
|
|
212 opl->write(1,0x20); // Enable waveform select (bit 5)
|
|
|
213
|
|
|
214 if( rol_header->mode == 0 )
|
|
|
215 {
|
|
|
216 opl->write( 0xbd, 0x20 ); // select rhythm mode (bit 5)
|
|
|
217 bdRegister = 0x20;
|
|
|
218
|
|
|
219 SetFreq( kTomtomChannel, 24 );
|
|
|
220 SetFreq( kSnareDrumChannel, 31 );
|
|
|
221 }
|
|
|
222
|
|
|
223 mNextTempoEvent = 0;
|
|
|
224 mCurrTick = 0;
|
|
|
225
|
|
|
226 SetRefresh(1.0f);
|
|
|
227 }
|
|
|
228 //---------------------------------------------------------
|
|
|
229 inline float fmin( int const a, int const b )
|
|
|
230 {
|
|
|
231 return static_cast<float>( a < b ? a : b );
|
|
|
232 }
|
|
|
233 //---------------------------------------------------------
|
|
|
234 void CrolPlayer::SetRefresh( float const multiplier )
|
|
|
235 {
|
|
|
236 float const tickBeat = fmin(kMaxTickBeat, rol_header->ticks_per_beat);
|
|
|
237
|
|
|
238 mRefresh = (tickBeat*rol_header->basic_tempo*multiplier) / 60.0f;
|
|
|
239 }
|
|
|
240 //---------------------------------------------------------
|
|
|
241 float CrolPlayer::getrefresh()
|
|
|
242 {
|
|
|
243 return mRefresh;
|
|
|
244 }
|
|
|
245 //---------------------------------------------------------
|
|
|
246 void CrolPlayer::UpdateVoice( int const voice, CVoiceData &voiceData )
|
|
|
247 {
|
|
|
248 TNoteEvents const &nEvents = voiceData.note_events;
|
|
|
249
|
|
|
250 if( nEvents.empty() || voiceData.mEventStatus & CVoiceData::kES_NoteEnd )
|
|
|
251 {
|
|
|
252 return; // no note data to process, don't bother doing anything.
|
|
|
253 }
|
|
|
254
|
|
|
255 TInstrumentEvents &iEvents = voiceData.instrument_events;
|
|
|
256 TVolumeEvents &vEvents = voiceData.volume_events;
|
|
|
257 TPitchEvents &pEvents = voiceData.pitch_events;
|
|
|
258
|
|
|
259 if( !(voiceData.mEventStatus & CVoiceData::kES_InstrEnd ) &&
|
|
|
260 iEvents[voiceData.next_instrument_event].time == mCurrTick )
|
|
|
261 {
|
|
|
262 if( voiceData.next_instrument_event < iEvents.size() )
|
|
|
263 {
|
|
|
264 send_ins_data_to_chip( voice, iEvents[voiceData.next_instrument_event].ins_index );
|
|
|
265 ++voiceData.next_instrument_event;
|
|
|
266 }
|
|
|
267 else
|
|
|
268 {
|
|
|
269 voiceData.mEventStatus |= CVoiceData::kES_InstrEnd;
|
|
|
270 }
|
|
|
271 }
|
|
|
272
|
|
|
273 if( !(voiceData.mEventStatus & CVoiceData::kES_VolumeEnd ) &&
|
|
|
274 vEvents[voiceData.next_volume_event].time == mCurrTick )
|
|
|
275 {
|
|
|
276 SVolumeEvent const &volumeEvent = vEvents[voiceData.next_volume_event];
|
|
|
277
|
|
|
278 if( voiceData.next_volume_event < vEvents.size() )
|
|
|
279 {
|
|
|
280 int const volume = (int)(63.0f*(1.0f - volumeEvent.multiplier));
|
|
|
281
|
|
|
282 SetVolume( voice, volume );
|
|
|
283
|
|
|
284 ++voiceData.next_volume_event; // move to next volume event
|
|
|
285 }
|
|
|
286 else
|
|
|
287 {
|
|
|
288 voiceData.mEventStatus |= CVoiceData::kES_VolumeEnd;
|
|
|
289 }
|
|
|
290 }
|
|
|
291
|
|
|
292 if( voiceData.mForceNote || voiceData.current_note_duration > voiceData.mNoteDuration-1 )
|
|
|
293 {
|
|
|
294 if( mCurrTick != 0 )
|
|
|
295 {
|
|
|
296 ++voiceData.current_note;
|
|
|
297 }
|
|
|
298
|
|
|
299 if( voiceData.current_note < nEvents.size() )
|
|
|
300 {
|
|
|
301 SNoteEvent const ¬eEvent = nEvents[voiceData.current_note];
|
|
|
302
|
|
|
303 SetNote( voice, noteEvent.number );
|
|
|
304 voiceData.current_note_duration = 0;
|
|
|
305 voiceData.mNoteDuration = noteEvent.duration;
|
|
|
306 voiceData.mForceNote = false;
|
|
|
307 }
|
|
|
308 else
|
|
|
309 {
|
|
|
310 SetNote( voice, kSilenceNote );
|
|
|
311 voiceData.mEventStatus |= CVoiceData::kES_NoteEnd;
|
|
|
312 return;
|
|
|
313 }
|
|
|
314 }
|
|
|
315
|
|
|
316 if( !(voiceData.mEventStatus & CVoiceData::kES_PitchEnd ) &&
|
|
|
317 pEvents[voiceData.next_pitch_event].time == mCurrTick )
|
|
|
318 {
|
|
|
319 if( voiceData.next_pitch_event < pEvents.size() )
|
|
|
320 {
|
|
|
321 SetPitch(voice,pEvents[voiceData.next_pitch_event].variation);
|
|
|
322 ++voiceData.next_pitch_event;
|
|
|
323 }
|
|
|
324 else
|
|
|
325 {
|
|
|
326 voiceData.mEventStatus |= CVoiceData::kES_PitchEnd;
|
|
|
327 }
|
|
|
328 }
|
|
|
329
|
|
|
330 ++voiceData.current_note_duration;
|
|
|
331 }
|
|
|
332 //---------------------------------------------------------
|
|
|
333 void CrolPlayer::SetNote( int const voice, int const note )
|
|
|
334 {
|
|
|
335 if( voice < kBassDrumChannel || rol_header->mode )
|
|
|
336 {
|
|
|
337 SetNoteMelodic( voice, note );
|
|
|
338 }
|
|
|
339 else
|
|
|
340 {
|
|
|
341 SetNotePercussive( voice, note );
|
|
|
342 }
|
|
|
343 }
|
|
|
344 //---------------------------------------------------------
|
|
|
345 void CrolPlayer::SetNotePercussive( int const voice, int const note )
|
|
|
346 {
|
|
|
347 int const bit_pos = 4-voice+kBassDrumChannel;
|
|
|
348
|
|
|
349 bdRegister &= ~( 1<<bit_pos );
|
|
|
350 opl->write( 0xbd, bdRegister );
|
|
|
351
|
|
|
352 if( note != kSilenceNote )
|
|
|
353 {
|
|
|
354 switch( voice )
|
|
|
355 {
|
|
|
356 case kTomtomChannel:
|
|
|
357 SetFreq( kSnareDrumChannel, note+7 );
|
|
|
358 case kBassDrumChannel:
|
|
|
359 SetFreq( voice, note );
|
|
|
360 break;
|
|
|
361 }
|
|
|
362
|
|
|
363 bdRegister |= 1<<bit_pos;
|
|
|
364 opl->write( 0xbd, bdRegister );
|
|
|
365 }
|
|
|
366 }
|
|
|
367 //---------------------------------------------------------
|
|
|
368 void CrolPlayer::SetNoteMelodic( int const voice, int const note )
|
|
|
369 {
|
|
|
370 opl->write( 0xb0+voice, bxRegister[voice] & ~0x20 );
|
|
|
371
|
|
|
372 if( note != kSilenceNote )
|
|
|
373 {
|
|
|
374 SetFreq( voice, note, true );
|
|
|
375 }
|
|
|
376 }
|
|
|
377 //---------------------------------------------------------
|
|
|
378 void CrolPlayer::SetPitch(int const voice, real32 const variation)
|
|
|
379 {
|
|
|
380 pitchCache[voice] = variation;
|
|
|
381 freqCache[voice] += (uint16)((((float)freqCache[voice])*(variation-1.0f)) / kPitchFactor);
|
|
|
382
|
|
|
383 opl->write(0xa0+voice,freqCache[voice] & 0xff);
|
|
|
384 }
|
|
|
385 //---------------------------------------------------------
|
|
|
386 void CrolPlayer::SetFreq( int const voice, int const note, bool const keyOn )
|
|
|
387 {
|
|
|
388 uint16 freq = kNoteTable[note%12] + ((note/12) << 10);
|
|
|
389 freq += (uint16)((((float)freq)*(pitchCache[voice]-1.0f))/kPitchFactor);
|
|
|
390
|
|
|
391 freqCache[voice] = freq;
|
|
|
392 bxRegister[voice] = ((freq >> 8) & 0x1f);
|
|
|
393
|
|
|
394 opl->write( 0xa0+voice, freq & 0xff );
|
|
|
395 opl->write( 0xb0+voice, bxRegister[voice] | (keyOn ? 0x20 : 0x0) );
|
|
|
396 }
|
|
|
397 //---------------------------------------------------------
|
|
|
398 void CrolPlayer::SetVolume( int const voice, int const volume )
|
|
|
399 {
|
|
|
400 volumeCache[voice] = (volumeCache[voice] &0xc0) | volume;
|
|
|
401
|
|
|
402 int const op_offset = ( voice < kSnareDrumChannel || rol_header->mode ) ?
|
|
|
403 op_table[voice]+3 : drum_table[voice-kSnareDrumChannel];
|
|
|
404
|
|
|
405 opl->write( 0x40+op_offset, volumeCache[voice] );
|
|
|
406 }
|
|
|
407 //---------------------------------------------------------
|
|
|
408 void CrolPlayer::send_ins_data_to_chip( int const voice, int const ins_index )
|
|
|
409 {
|
|
|
410 SRolInstrument &instrument = ins_list[ins_index].instrument;
|
|
|
411
|
|
|
412 send_operator( voice, instrument.modulator, instrument.carrier );
|
|
|
413 }
|
|
|
414 //---------------------------------------------------------
|
|
|
415 void CrolPlayer::send_operator( int const voice, SOPL2Op const &modulator, SOPL2Op const &carrier )
|
|
|
416 {
|
|
|
417 if( voice < kSnareDrumChannel || rol_header->mode )
|
|
|
418 {
|
|
|
419 int const op_offset = op_table[voice];
|
|
|
420
|
|
|
421 opl->write( 0x20+op_offset, modulator.ammulti );
|
|
|
422 opl->write( 0x40+op_offset, modulator.ksltl );
|
|
|
423 opl->write( 0x60+op_offset, modulator.ardr );
|
|
|
424 opl->write( 0x80+op_offset, modulator.slrr );
|
|
|
425 opl->write( 0xc0+voice , modulator.fbc );
|
|
|
426 opl->write( 0xe0+op_offset, modulator.waveform );
|
|
|
427
|
|
|
428 volumeCache[voice] = (carrier.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
|
|
|
429
|
|
|
430 opl->write( 0x23+op_offset, carrier.ammulti );
|
|
|
431 opl->write( 0x43+op_offset, volumeCache[voice] );
|
|
|
432 opl->write( 0x63+op_offset, carrier.ardr );
|
|
|
433 opl->write( 0x83+op_offset, carrier.slrr );
|
|
|
434 // opl->write( 0xc3+voice , carrier.fbc ); <- don't bother writing this.
|
|
|
435 opl->write( 0xe3+op_offset, carrier.waveform );
|
|
|
436 }
|
|
|
437 else
|
|
|
438 {
|
|
|
439 int const op_offset = drum_table[voice-kSnareDrumChannel];
|
|
|
440
|
|
|
441 volumeCache[voice] = (modulator.ksltl & 0xc0) | volumeCache[voice] & 0x3f;
|
|
|
442
|
|
|
443 opl->write( 0x20+op_offset, modulator.ammulti );
|
|
|
444 opl->write( 0x40+op_offset, volumeCache[voice] );
|
|
|
445 opl->write( 0x60+op_offset, modulator.ardr );
|
|
|
446 opl->write( 0x80+op_offset, modulator.slrr );
|
|
|
447 opl->write( 0xc0+voice , modulator.fbc );
|
|
|
448 opl->write( 0xe0+op_offset, modulator.waveform );
|
|
|
449 }
|
|
|
450 }
|
|
|
451 //---------------------------------------------------------
|
|
|
452 void CrolPlayer::load_tempo_events( binistream *f )
|
|
|
453 {
|
|
|
454 int16 const num_tempo_events = f->readInt( 2 );
|
|
|
455
|
|
|
456 mTempoEvents.reserve( num_tempo_events );
|
|
|
457
|
|
|
458 for(int i=0; i<num_tempo_events; ++i)
|
|
|
459 {
|
|
|
460 STempoEvent event;
|
|
|
461
|
|
|
462 event.time = f->readInt( 2 );
|
|
|
463 event.multiplier = f->readFloat( binio::Single );
|
|
|
464 mTempoEvents.push_back( event );
|
|
|
465 }
|
|
|
466 }
|
|
|
467 //---------------------------------------------------------
|
|
|
468 bool CrolPlayer::load_voice_data( binistream *f, std::string const &bnk_filename, const CFileProvider &fp )
|
|
|
469 {
|
|
|
470 SBnkHeader bnk_header;
|
|
|
471 binistream *bnk_file = fp.open( bnk_filename.c_str() );
|
|
|
472
|
|
|
473 if( bnk_file )
|
|
|
474 {
|
|
|
475 load_bnk_info( bnk_file, bnk_header );
|
|
|
476
|
|
|
477 int const numVoices = rol_header->mode ? kNumMelodicVoices : kNumPercussiveVoices;
|
|
|
478
|
|
|
479 voice_data.reserve( numVoices );
|
|
|
480 for(int i=0; i<numVoices; ++i)
|
|
|
481 {
|
|
|
482 CVoiceData voice;
|
|
|
483
|
|
|
484 load_note_events( f, voice );
|
|
|
485 load_instrument_events( f, voice, bnk_file, bnk_header );
|
|
|
486 load_volume_events( f, voice );
|
|
|
487 load_pitch_events( f, voice );
|
|
|
488
|
|
|
489 voice_data.push_back( voice );
|
|
|
490 }
|
|
|
491
|
|
|
492 fp.close(bnk_file);
|
|
|
493
|
|
|
494 return true;
|
|
|
495 }
|
|
|
496
|
|
|
497 return false;
|
|
|
498 }
|
|
|
499 //---------------------------------------------------------
|
|
|
500 void CrolPlayer::load_note_events( binistream *f, CVoiceData &voice )
|
|
|
501 {
|
|
|
502 f->seek( 15, binio::Add );
|
|
|
503
|
|
|
504 int16 const time_of_last_note = f->readInt( 2 );
|
|
|
505
|
|
|
506 if( time_of_last_note != 0 )
|
|
|
507 {
|
|
|
508 TNoteEvents ¬e_events = voice.note_events;
|
|
|
509 int16 total_duration = 0;
|
|
|
510
|
|
|
511 do
|
|
|
512 {
|
|
|
513 SNoteEvent event;
|
|
|
514
|
|
|
515 event.number = f->readInt( 2 );
|
|
|
516 event.duration = f->readInt( 2 );
|
|
|
517
|
|
|
518 event.number += kSilenceNote; // adding -12
|
|
|
519
|
|
|
520 note_events.push_back( event );
|
|
|
521
|
|
|
522 total_duration += event.duration;
|
|
|
523 } while( total_duration < time_of_last_note );
|
|
|
524
|
|
|
525 if( time_of_last_note > mTimeOfLastNote )
|
|
|
526 {
|
|
|
527 mTimeOfLastNote = time_of_last_note;
|
|
|
528 }
|
|
|
529 }
|
|
|
530
|
|
|
531 f->seek( 15, binio::Add );
|
|
|
532 }
|
|
|
533 //---------------------------------------------------------
|
|
|
534 void CrolPlayer::load_instrument_events( binistream *f, CVoiceData &voice,
|
|
|
535 binistream *bnk_file, SBnkHeader const &bnk_header )
|
|
|
536 {
|
|
|
537 int16 const number_of_instrument_events = f->readInt( 2 );
|
|
|
538
|
|
|
539 TInstrumentEvents &instrument_events = voice.instrument_events;
|
|
|
540
|
|
|
541 instrument_events.reserve( number_of_instrument_events );
|
|
|
542
|
|
|
543 for(int i=0; i<number_of_instrument_events; ++i)
|
|
|
544 {
|
|
|
545 SInstrumentEvent event;
|
|
|
546 event.time = f->readInt( 2 );
|
|
|
547 f->readString( event.name, 9 );
|
|
|
548
|
|
|
549 std::string event_name = event.name;
|
|
|
550 event.ins_index = load_rol_instrument( bnk_file, bnk_header, event_name );
|
|
|
551
|
|
|
552 instrument_events.push_back( event );
|
|
|
553
|
|
|
554 f->seek( 1+2, binio::Add );
|
|
|
555 }
|
|
|
556
|
|
|
557 f->seek( 15, binio::Add );
|
|
|
558 }
|
|
|
559 //---------------------------------------------------------
|
|
|
560 void CrolPlayer::load_volume_events( binistream *f, CVoiceData &voice )
|
|
|
561 {
|
|
|
562 int16 const number_of_volume_events = f->readInt( 2 );
|
|
|
563
|
|
|
564 TVolumeEvents &volume_events = voice.volume_events;
|
|
|
565
|
|
|
566 volume_events.reserve( number_of_volume_events );
|
|
|
567
|
|
|
568 for(int i=0; i<number_of_volume_events; ++i)
|
|
|
569 {
|
|
|
570 SVolumeEvent event;
|
|
|
571 event.time = f->readInt( 2 );
|
|
|
572 event.multiplier = f->readFloat( binio::Single );
|
|
|
573
|
|
|
574 volume_events.push_back( event );
|
|
|
575 }
|
|
|
576
|
|
|
577 f->seek( 15, binio::Add );
|
|
|
578 }
|
|
|
579 //---------------------------------------------------------
|
|
|
580 void CrolPlayer::load_pitch_events( binistream *f, CVoiceData &voice )
|
|
|
581 {
|
|
|
582 int16 const number_of_pitch_events = f->readInt( 2 );
|
|
|
583
|
|
|
584 TPitchEvents &pitch_events = voice.pitch_events;
|
|
|
585
|
|
|
586 pitch_events.reserve( number_of_pitch_events );
|
|
|
587
|
|
|
588 for(int i=0; i<number_of_pitch_events; ++i)
|
|
|
589 {
|
|
|
590 SPitchEvent event;
|
|
|
591 event.time = f->readInt( 2 );
|
|
|
592 event.variation = f->readFloat( binio::Single );
|
|
|
593
|
|
|
594 pitch_events.push_back( event );
|
|
|
595 }
|
|
|
596 }
|
|
|
597 //---------------------------------------------------------
|
|
|
598 bool CrolPlayer::load_bnk_info( binistream *f, SBnkHeader &header )
|
|
|
599 {
|
|
|
600 header.version_major = f->readInt(1);
|
|
|
601 header.version_minor = f->readInt(1);
|
|
|
602 f->readString( header.signature, 6 );
|
|
|
603
|
|
|
604 header.number_of_list_entries_used = f->readInt( 2 );
|
|
|
605 header.total_number_of_list_entries = f->readInt( 2 );
|
|
|
606
|
|
|
607 header.abs_offset_of_name_list = f->readInt( 4 );
|
|
|
608 header.abs_offset_of_data = f->readInt( 4 );
|
|
|
609
|
|
|
610 f->seek( header.abs_offset_of_name_list, binio::Set );
|
|
|
611
|
|
|
612 TInstrumentNames &ins_name_list = header.ins_name_list;
|
|
|
613 ins_name_list.reserve( header.number_of_list_entries_used );
|
|
|
614
|
|
|
615 for(int i=0; i<header.number_of_list_entries_used; ++i)
|
|
|
616 {
|
|
|
617 SInstrumentName instrument;
|
|
|
618
|
|
|
619 instrument.index = f->readInt( 2 );
|
|
|
620 instrument.record_used = f->readInt(1);
|
|
|
621 f->readString( instrument.name, 9 );
|
|
|
622
|
|
|
623 // printf("%s = #%d\n", instrument.name, i );
|
|
|
624
|
|
|
625 ins_name_list.push_back( instrument );
|
|
|
626 }
|
|
|
627
|
|
|
628 //std::sort( ins_name_list.begin(), ins_name_list.end(), StringCompare() );
|
|
|
629
|
|
|
630 return true;
|
|
|
631 }
|
|
|
632 //---------------------------------------------------------
|
|
|
633 int CrolPlayer::load_rol_instrument( binistream *f, SBnkHeader const &header, std::string &name )
|
|
|
634 {
|
|
|
635 TInstrumentNames const &ins_name_list = header.ins_name_list;
|
|
|
636
|
|
|
637 int const ins_index = get_ins_index( name );
|
|
|
638
|
|
|
639 if( ins_index != -1 )
|
|
|
640 {
|
|
|
641 return ins_index;
|
|
|
642 }
|
|
|
643
|
|
|
644 typedef TInstrumentNames::const_iterator TInsIter;
|
|
|
645 typedef std::pair<TInsIter, TInsIter> TInsIterPair;
|
|
|
646
|
|
|
647 TInsIterPair range = std::equal_range( ins_name_list.begin(),
|
|
|
648 ins_name_list.end(),
|
|
|
649 name,
|
|
|
650 StringCompare() );
|
|
|
651
|
|
|
652 if( range.first != range.second )
|
|
|
653 {
|
|
|
654 int const seekOffs = header.abs_offset_of_data + (range.first->index*kSizeofDataRecord);
|
|
|
655 f->seek( seekOffs, binio::Set );
|
|
|
656 }
|
|
|
657
|
|
|
658 SUsedList usedIns;
|
|
|
659 usedIns.name = name;
|
|
|
660
|
|
|
661 if( range.first != range.second )
|
|
|
662 {
|
|
|
663 read_rol_instrument( f, usedIns.instrument );
|
|
|
664 }
|
|
|
665 else
|
|
|
666 {
|
|
|
667 // set up default instrument data here
|
|
|
668 memset( &usedIns.instrument, 0, kSizeofDataRecord );
|
|
|
669 }
|
|
|
670 ins_list.push_back( usedIns );
|
|
|
671
|
|
|
672 return ins_list.size()-1;
|
|
|
673 }
|
|
|
674 //---------------------------------------------------------
|
|
|
675 int CrolPlayer::get_ins_index( std::string const &name ) const
|
|
|
676 {
|
|
|
677 for(unsigned int i=0; i<ins_list.size(); ++i)
|
|
|
678 {
|
|
|
679 if( stricmp(ins_list[i].name.c_str(), name.c_str()) == 0 )
|
|
|
680 {
|
|
|
681 return i;
|
|
|
682 }
|
|
|
683 }
|
|
|
684
|
|
|
685 return -1;
|
|
|
686 }
|
|
|
687 //---------------------------------------------------------
|
|
|
688 void CrolPlayer::read_rol_instrument( binistream *f, SRolInstrument &ins )
|
|
|
689 {
|
|
|
690 ins.mode = f->readInt(1);
|
|
|
691 ins.voice_number = f->readInt(1);
|
|
|
692
|
|
|
693 read_fm_operator( f, ins.modulator );
|
|
|
694 read_fm_operator( f, ins.carrier );
|
|
|
695
|
|
|
696 ins.modulator.waveform = f->readInt(1);
|
|
|
697 ins.carrier.waveform = f->readInt(1);
|
|
|
698 }
|
|
|
699 //---------------------------------------------------------
|
|
|
700 void CrolPlayer::read_fm_operator( binistream *f, SOPL2Op &opl2_op )
|
|
|
701 {
|
|
|
702 SFMOperator fm_op;
|
|
|
703
|
|
|
704 fm_op.key_scale_level = f->readInt(1);
|
|
|
705 fm_op.freq_multiplier = f->readInt(1);
|
|
|
706 fm_op.feed_back = f->readInt(1);
|
|
|
707 fm_op.attack_rate = f->readInt(1);
|
|
|
708 fm_op.sustain_level = f->readInt(1);
|
|
|
709 fm_op.sustaining_sound = f->readInt(1);
|
|
|
710 fm_op.decay_rate = f->readInt(1);
|
|
|
711 fm_op.release_rate = f->readInt(1);
|
|
|
712 fm_op.output_level = f->readInt(1);
|
|
|
713 fm_op.amplitude_vibrato = f->readInt(1);
|
|
|
714 fm_op.frequency_vibrato = f->readInt(1);
|
|
|
715 fm_op.envelope_scaling = f->readInt(1);
|
|
|
716 fm_op.fm_type = f->readInt(1);
|
|
|
717
|
|
|
718 opl2_op.ammulti = fm_op.amplitude_vibrato << 7 | fm_op.frequency_vibrato << 6 | fm_op.sustaining_sound << 5 | fm_op.envelope_scaling << 4 | fm_op.freq_multiplier;
|
|
|
719 opl2_op.ksltl = fm_op.key_scale_level << 6 | fm_op.output_level;
|
|
|
720 opl2_op.ardr = fm_op.attack_rate << 4 | fm_op.decay_rate;
|
|
|
721 opl2_op.slrr = fm_op.sustain_level << 4 | fm_op.release_rate;
|
|
|
722 opl2_op.fbc = fm_op.feed_back << 1 | (fm_op.fm_type ^ 1);
|
|
|
723 }
|