comparison src/Input/adplug/core/rol.cxx @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13389e613d67
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 &noteEvent = 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 &note_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 }