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