forked from mattiasgustavsson/libs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
opl.h
2896 lines (2409 loc) · 120 KB
/
opl.h
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
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
------------------------------------------------------------------------------
Licensing information can be found at the end of the file.
------------------------------------------------------------------------------
opl.h - v0.1 - OPL3 (SoundBlaster16) emulation with MIDI compatible interface
Do this:
#define OP_IMPLEMENTATION
before you include this file in *one* C/C++ file to create the implementation.
------------------------------------------------------------------------------
This file contains heavily modified code from two different open source
projects: ymfm https://github.com/aaronsgiles/ymfm (a collection of Yamaha FM
sound cores) and DOSMid http://dosmid.sourceforge.net (a MIDI and MUS player
for DOS).
In the case of the ymfm code, I reduced it down to just the OPL3 parts, and
refactored the remaining code from OOP C++ to plain C, and prefixed everything
with OPL_EMU_ or opl_emu_.
For the DOSMidi code, I pretty much used the files OPL.H and OPL.C with only
minor changes, but put it together with the ymfm code to form a single file
library.
It is possible (even likely) that I have messed things up in this process, and
I recommend that you use the original source code versions linked above if you
want to use this for anything serious.
/ Mattias Gustavsson ( mattias@mattiasgustavsson.com )
*/
#ifndef opl_h
#define opl_h
typedef struct opl_timbre_t {
unsigned long modulator_E862, carrier_E862;
unsigned char modulator_40, carrier_40;
unsigned char feedconn;
signed char finetune;
unsigned char notenum;
signed short noteoffset;
} opl_timbre_t;
typedef struct opl_t opl_t;
/* Initialize hardware upon startup - positive on success, negative otherwise
* Returns 0 for OPL2 initialization, or 1 if OPL3 has been detected */
opl_t* opl_create( void );
/* close OPL device */
void opl_destroy( opl_t* opl );
/* turns off all notes */
void opl_clear( opl_t* opl );
/* turn note 'on', on emulated MIDI channel */
void opl_midi_noteon( opl_t* opl, int channel, int note, int velocity );
/* turn note 'off', on emulated MIDI channel */
void opl_midi_noteoff( opl_t* opl, int channel, int note );
/* adjust the pitch wheel on emulated MIDI channel */
void opl_midi_pitchwheel( opl_t* opl, int channel, int wheelvalue );
/* emulate MIDI 'controller' messages on the OPL */
void opl_midi_controller( opl_t* opl, int channel, int id, int value );
/* assign a new instrument to emulated MIDI channel */
void opl_midi_changeprog( opl_t* opl, int channel, int program );
void opl_loadinstrument(opl_t* opl, int voice, opl_timbre_t* timbre );
/* loads an IBK bank from file into an array of 128 opl_timbre_t structures.
* returns 0 on success, non-zero otherwise */
int opl_loadbank_ibk( opl_t* opl, char const* file );
int opl_loadbank_op2(opl_t* opl, void const* data, int size );
void opl_render( opl_t* opl, short* sample_pairs, int sample_pairs_count, float volume );
void opl_write( opl_t* opl, int count, unsigned short* regs, unsigned char* data );
#endif /* opl_h */
#ifdef OPL_IMPLEMENTATION
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> /* calloc() */
#include <string.h> /* strdup() */
//-------------------------------------------------
// bitfield - extract a bitfield from the given
// value, starting at bit 'start' for a length of
// 'length' bits
//-------------------------------------------------
uint32_t opl_emu_bitfield(uint32_t value, int start, int length )
{
return (value >> start) & ((1 << length) - 1);
}
//-------------------------------------------------
// clamp - clamp between the minimum and maximum
// values provided
//-------------------------------------------------
int32_t opl_emu_clamp(int32_t value, int32_t minval, int32_t maxval)
{
if (value < minval)
return minval;
if (value > maxval)
return maxval;
return value;
}
#define opl_min(a,b) (((a)<(b))?(a):(b))
#define opl_max(a,b) (((a)>(b))?(a):(b))
// various envelope states
enum opl_emu_envelope_state
{
OPL_EMU_EG_ATTACK = 1,
OPL_EMU_EG_DECAY = 2,
OPL_EMU_EG_SUSTAIN = 3,
OPL_EMU_EG_RELEASE = 4,
OPL_EMU_EG_STATES = 6
};
// three different keyon sources; actual keyon is an OR over all of these
enum opl_emu_keyon_type
{
OPL_EMU_KEYON_NORMAL = 0,
OPL_EMU_KEYON_RHYTHM = 1,
OPL_EMU_KEYON_CSM = 2
};
// ======================> opl_emu_opdata_cache
#define OPL_EMU_PHASE_STEP_DYNAMIC 1
// this class holds data that is computed once at the start of clocking
// and remains static during subsequent sound generation
struct opl_emu_opdata_cache
{
// set phase_step to this value to recalculate it each sample; needed
// in the case of PM LFO changes
uint16_t const *waveform; // base of sine table
uint32_t phase_step; // phase step, or OPL_EMU_PHASE_STEP_DYNAMIC if PM is active
uint32_t total_level; // total level * 8 + KSL
uint32_t block_freq; // raw block frequency value (used to compute phase_step)
int32_t detune; // detuning value (used to compute phase_step)
uint32_t multiple; // multiple value (x.1, used to compute phase_step)
uint32_t eg_sustain; // sustain level, shifted up to envelope values
uint8_t eg_rate[OPL_EMU_EG_STATES]; // envelope rate, including KSR
uint8_t eg_shift; // envelope shift amount
};
// ======================> opl_emu_registers
//
// OPL/OPL2/OPL3/OPL4 register map:
//
// System-wide registers:
// 01 xxxxxxxx Test register
// --x----- Enable OPL compatibility mode [OPL2 only] (1 = enable)
// 02 xxxxxxxx Timer A value (4 * OPN)
// 03 xxxxxxxx Timer B value
// 04 x------- RST
// -x------ Mask timer A
// --x----- Mask timer B
// ------x- Load timer B
// -------x Load timer A
// 08 x------- CSM mode [OPL/OPL2 only]
// -x------ Note select
// BD x------- AM depth
// -x------ PM depth
// --x----- Rhythm enable
// ---x---- Bass drum key on
// ----x--- Snare drum key on
// -----x-- Tom key on
// ------x- Top cymbal key on
// -------x High hat key on
// 101 --xxxxxx Test register 2 [OPL3 only]
// 104 --x----- Channel 6 4-operator mode [OPL3 only]
// ---x---- Channel 5 4-operator mode [OPL3 only]
// ----x--- Channel 4 4-operator mode [OPL3 only]
// -----x-- Channel 3 4-operator mode [OPL3 only]
// ------x- Channel 2 4-operator mode [OPL3 only]
// -------x Channel 1 4-operator mode [OPL3 only]
// 105 -------x New [OPL3 only]
// ------x- New2 [OPL4 only]
//
// Per-channel registers (channel in address bits 0-3)
// Note that all these apply to address+100 as well on OPL3+
// A0-A8 xxxxxxxx F-number (low 8 bits)
// B0-B8 --x----- Key on
// ---xxx-- Block (octvate, 0-7)
// ------xx F-number (high two bits)
// C0-C8 x------- CHD output (to DO0 pin) [OPL3+ only]
// -x------ CHC output (to DO0 pin) [OPL3+ only]
// --x----- CHB output (mixed right, to DO2 pin) [OPL3+ only]
// ---x---- CHA output (mixed left, to DO2 pin) [OPL3+ only]
// ----xxx- Feedback level for operator 1 (0-7)
// -------x Operator connection algorithm
//
// Per-operator registers (operator in bits 0-5)
// Note that all these apply to address+100 as well on OPL3+
// 20-35 x------- AM enable
// -x------ PM enable (VIB)
// --x----- EG type
// ---x---- Key scale rate
// ----xxxx Multiple value (0-15)
// 40-55 xx------ Key scale level (0-3)
// --xxxxxx Total level (0-63)
// 60-75 xxxx---- Attack rate (0-15)
// ----xxxx Decay rate (0-15)
// 80-95 xxxx---- Sustain level (0-15)
// ----xxxx Release rate (0-15)
// E0-F5 ------xx Wave select (0-3) [OPL2 only]
// -----xxx Wave select (0-7) [OPL3+ only]
//
// constants
#define OPL_EMU_REGISTERS_OUTPUTS 2
#define OPL_EMU_REGISTERS_CHANNELS 18
#define OPL_EMU_REGISTERS_ALL_CHANNELS ( (1 << OPL_EMU_REGISTERS_CHANNELS) - 1 )
#define OPL_EMU_REGISTERS_OPERATORS ( OPL_EMU_REGISTERS_CHANNELS * 2 )
#define OPL_EMU_REGISTERS_WAVEFORMS 8
#define OPL_EMU_REGISTERS_REGISTERS 0x200
#define OPL_EMU_REGISTERS_REG_MODE 0x04
#define OPL_EMU_REGISTERS_DEFAULT_PRESCALE 8
#define OPL_EMU_REGISTERS_STATUS_BUSY 0
// this value is returned from the write() function for rhythm channels
#define OPL_EMU_REGISTERS_RHYTHM_CHANNEL 0xff
// this is the size of a full sin waveform
#define OPL_EMU_REGISTERS_WAVEFORM_LENGTH 0x400
struct opl_emu_registers
{
// internal state
uint16_t m_lfo_am_counter; // LFO AM counter
uint16_t m_lfo_pm_counter; // LFO PM counter
uint32_t m_noise_lfsr; // noise LFSR state
uint8_t m_lfo_am; // current LFO AM value
uint8_t m_regdata[OPL_EMU_REGISTERS_REGISTERS]; // register data
uint16_t m_waveform[OPL_EMU_REGISTERS_WAVEFORMS][OPL_EMU_REGISTERS_WAVEFORM_LENGTH]; // waveforms
};
// helper to encode four operator numbers into a 32-bit value in the
// operator maps for each register class
uint32_t opl_emu_registers_operator_list(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4)
{
return o1 | (o2 << 8) | (o3 << 16) | (o4 << 24);
}
// helper to apply KSR to the raw ADSR rate, ignoring ksr if the
// raw value is 0, and clamping to 63
uint32_t opl_emu_registers_effective_rate(uint32_t rawrate, uint32_t ksr)
{
return (rawrate == 0) ? 0 : opl_min(rawrate + ksr, 63);
}
// constructor
void opl_emu_registers_init( struct opl_emu_registers* regs );
// reset to initial state
void opl_emu_registers_reset(struct opl_emu_registers* regs);
// map channel number to register offset
uint32_t opl_emu_registers_channel_offset(uint32_t chnum)
{
return (chnum % 9) + 0x100 * (chnum / 9);
}
// map operator number to register offset
uint32_t opl_emu_registers_operator_offset(uint32_t opnum)
{
return (opnum % 18) + 2 * ((opnum % 18) / 6) + 0x100 * (opnum / 18);
}
// return an array of operator indices for each channel
struct opl_emu_registers_operator_mapping { uint32_t chan[OPL_EMU_REGISTERS_CHANNELS]; };
void opl_emu_registers_operator_map(struct opl_emu_registers* regs,struct opl_emu_registers_operator_mapping* dest) ;
// handle writes to the register array
int opl_emu_registers_write(struct opl_emu_registers* regs,uint16_t index, uint8_t data, uint32_t* chan, uint32_t* opmask);
// clock the noise and LFO, if present, returning LFO PM value
int32_t opl_emu_registers_clock_noise_and_lfo(struct opl_emu_registers* regs);
// reset the LFO
void opl_emu_registers_reset_lfo(struct opl_emu_registers* regs) { regs->m_lfo_am_counter = regs->m_lfo_pm_counter = 0; }
// return the AM offset from LFO for the given channel
// on OPL this is just a fixed value
uint32_t opl_emu_registers_lfo_am_offset(struct opl_emu_registers* regs,uint32_t choffs) { return regs->m_lfo_am; }
// return LFO/noise states
uint32_t opl_emu_registers_noise_state(struct opl_emu_registers* regs) { return regs->m_noise_lfsr >> 23; }
// caching helpers
void opl_emu_registers_cache_operator_data(struct opl_emu_registers* regs,uint32_t choffs, uint32_t opoffs, struct opl_emu_opdata_cache *cache);
// compute the phase step, given a PM value
uint32_t opl_emu_registers_compute_phase_step(struct opl_emu_registers* regs,uint32_t choffs, uint32_t opoffs, struct opl_emu_opdata_cache const *cache, int32_t lfo_raw_pm);
// return a bitfield extracted from a byte
uint32_t opl_emu_registers_byte(struct opl_emu_registers* regs,uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset/* = 0*/)
{
return opl_emu_bitfield(regs->m_regdata[offset + extra_offset], start, count);
}
// return a bitfield extracted from a pair of bytes, MSBs listed first
uint32_t opl_emu_registers_word(struct opl_emu_registers* regs,uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset/* = 0*/)
{
return (opl_emu_registers_byte(regs,offset1, start1, count1, extra_offset) << count2) | opl_emu_registers_byte(regs,offset2, start2, count2, extra_offset);
}
// system-wide registers
uint32_t opl_emu_registers_timer_a_value(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x02, 0, 8, 0) * 4; } // 8->10 bits
uint32_t opl_emu_registers_timer_b_value(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x03, 0, 8, 0); }
uint32_t opl_emu_registers_status_mask(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x04, 0, 8, 0) & 0x78; }
uint32_t opl_emu_registers_note_select(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x08, 6, 1, 0); }
uint32_t opl_emu_registers_lfo_am_depth(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0xbd, 7, 1, 0); }
uint32_t opl_emu_registers_lfo_pm_depth(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0xbd, 6, 1, 0); }
uint32_t opl_emu_registers_rhythm_enable(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0xbd, 5, 1, 0); }
uint32_t opl_emu_registers_newflag(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x105, 0, 1, 0); }
uint32_t opl_emu_registers_fourop_enable(struct opl_emu_registers* regs) { return opl_emu_registers_byte(regs,0x104, 0, 6, 0); }
// per-channel registers
uint32_t opl_emu_registers_ch_block_freq(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_word(regs,0xb0, 0, 5, 0xa0, 0, 8, choffs); }
uint32_t opl_emu_registers_ch_feedback(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_byte(regs,0xc0, 1, 3, choffs); }
uint32_t opl_emu_registers_ch_algorithm(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_byte(regs,0xc0, 0, 1, choffs) | ((8 | (opl_emu_registers_byte(regs,0xc3, 0, 1, choffs) << 1))); }
uint32_t opl_emu_registers_ch_output_any(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_newflag(regs) ? opl_emu_registers_byte(regs,0xc0 + choffs, 4, 4, 0) : 1; }
uint32_t opl_emu_registers_ch_output_0(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_newflag(regs) ? opl_emu_registers_byte(regs,0xc0 + choffs, 4, 1, 0) : 1; }
uint32_t opl_emu_registers_ch_output_1(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_newflag(regs) ? opl_emu_registers_byte(regs,0xc0 + choffs, 5, 1, 0) : 1; }
uint32_t opl_emu_registers_ch_output_2(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_newflag(regs) ? opl_emu_registers_byte(regs,0xc0 + choffs, 6, 1, 0) : 0; }
uint32_t opl_emu_registers_ch_output_3(struct opl_emu_registers* regs,uint32_t choffs) { return opl_emu_registers_newflag(regs) ? opl_emu_registers_byte(regs,0xc0 + choffs, 7, 1, 0) : 0; }
// per-operator registers
uint32_t opl_emu_registers_op_lfo_am_enable(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x20, 7, 1, opoffs); }
uint32_t opl_emu_registers_op_lfo_pm_enable(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x20, 6, 1, opoffs); }
uint32_t opl_emu_registers_op_eg_sustain(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x20, 5, 1, opoffs); }
uint32_t opl_emu_registers_op_ksr(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x20, 4, 1, opoffs); }
uint32_t opl_emu_registers_op_multiple(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x20, 0, 4, opoffs); }
uint32_t opl_emu_registers_op_ksl(struct opl_emu_registers* regs,uint32_t opoffs) { uint32_t temp = opl_emu_registers_byte(regs,0x40, 6, 2, opoffs); return opl_emu_bitfield(temp, 1,1) | (opl_emu_bitfield(temp, 0,1) << 1); }
uint32_t opl_emu_registers_op_total_level(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x40, 0, 6, opoffs); }
uint32_t opl_emu_registers_op_attack_rate(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x60, 4, 4, opoffs); }
uint32_t opl_emu_registers_op_decay_rate(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x60, 0, 4, opoffs); }
uint32_t opl_emu_registers_op_sustain_level(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x80, 4, 4, opoffs); }
uint32_t opl_emu_registers_op_release_rate(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0x80, 0, 4, opoffs); }
uint32_t opl_emu_registers_op_waveform(struct opl_emu_registers* regs,uint32_t opoffs) { return opl_emu_registers_byte(regs,0xe0, 0, opl_emu_registers_newflag(regs) ? 3 : 2, opoffs); }
// helper to determine if the this channel is an active rhythm channel
int opl_emu_registers_is_rhythm(struct opl_emu_registers* regs,uint32_t choffs)
{
return opl_emu_registers_rhythm_enable(regs) && (choffs >= 6 && choffs <= 8);
}
// ======================> opl_emu_fm_operator
// "quiet" value, used to optimize when we can skip doing working
#define OPL_EMU_FM_OPERATOR_EG_QUIET 0x200
// opl_emu_fm_operator represents an FM operator (or "slot" in FM parlance), which
// produces an output sine wave modulated by an envelope
struct opl_emu_fm_operator
{
// internal state
uint32_t m_choffs; // channel offset in registers
uint32_t m_opoffs; // operator offset in registers
uint32_t m_phase; // current phase value (10.10 format)
uint16_t m_env_attenuation; // computed envelope attenuation (4.6 format)
enum opl_emu_envelope_state m_env_state; // current envelope state
uint8_t m_key_state; // current key state: on or off (bit 0)
uint8_t m_keyon_live; // live key on state (bit 0 = direct, bit 1 = rhythm, bit 2 = CSM)
struct opl_emu_opdata_cache m_cache; // cached values for performance
struct opl_emu_registers* m_regs; // direct reference to registers
};
void opl_emu_fm_operator_init(struct opl_emu_fm_operator* fmop, struct opl_emu_registers* regs, uint32_t opoffs);
// reset the operator state
void opl_emu_fm_operator_reset(struct opl_emu_fm_operator* fmop);
// return the operator/channel offset
uint32_t opl_emu_fm_operator_opoffs(struct opl_emu_fm_operator* fmop) { return fmop->m_opoffs; }
uint32_t opl_emu_fm_operator_choffs(struct opl_emu_fm_operator* fmop) { return fmop->m_choffs; }
// set the current channel
void opl_emu_fm_operator_set_choffs(struct opl_emu_fm_operator* fmop,uint32_t choffs) { fmop->m_choffs = choffs; }
// prepare prior to clocking
int opl_emu_fm_operator_prepare(struct opl_emu_fm_operator* fmop);
// master clocking function
void opl_emu_fm_operator_clock(struct opl_emu_fm_operator* fmop,uint32_t env_counter, int32_t lfo_raw_pm);
// return the current phase value
uint32_t opl_emu_fm_operator_phase(struct opl_emu_fm_operator* fmop) { return fmop->m_phase >> 10; }
// compute operator volume
int32_t opl_emu_fm_operator_compute_volume(struct opl_emu_fm_operator* fmop,uint32_t phase, uint32_t am_offset) ;
// key state control
void opl_emu_fm_operator_keyonoff(struct opl_emu_fm_operator* fmop,uint32_t on, enum opl_emu_keyon_type type);
// start the attack phase
void opl_emu_fm_operator_start_attack(struct opl_emu_fm_operator* fmop);
// start the release phase
void opl_emu_fm_operator_start_release(struct opl_emu_fm_operator* fmop);
// clock phases
void opl_emu_fm_operator_clock_keystate(struct opl_emu_fm_operator* fmop,uint32_t keystate);
void opl_emu_fm_operator_clock_envelope(struct opl_emu_fm_operator* fmop,uint32_t env_counter);
void opl_emu_fm_operator_clock_phase(struct opl_emu_fm_operator* fmop,int32_t lfo_raw_pm);
// return effective attenuation of the envelope
uint32_t opl_emu_fm_operator_envelope_attenuation(struct opl_emu_fm_operator* fmop,uint32_t am_offset) ;
// ======================> opl_emu_fm_channel
// opl_emu_fm_channel represents an FM channel which combines the output of 2 or 4
// operators into a final result
struct opl_emu_fm_channel
{
// internal state
uint32_t m_choffs; // channel offset in registers
int16_t m_feedback[2]; // feedback memory for operator 1
int16_t m_feedback_in; // next input value for op 1 feedback (set in output)
struct opl_emu_fm_operator *m_op[4]; // up to 4 operators
struct opl_emu_registers* m_regs; // direct reference to registers
};
void opl_emu_fm_channel_init(struct opl_emu_fm_channel* fmch,struct opl_emu_registers* regs, uint32_t choffs);
// reset the channel state
void opl_emu_fm_channel_reset(struct opl_emu_fm_channel* fmch);
// return the channel offset
uint32_t opl_emu_fm_channel_choffs(struct opl_emu_fm_channel* fmch) { return fmch->m_choffs; }
// assign operators
void opl_emu_fm_channel_assign(struct opl_emu_fm_channel* fmch,uint32_t index, struct opl_emu_fm_operator *op)
{
fmch->m_op[index] = op;
if (op != NULL)
opl_emu_fm_operator_set_choffs(op, fmch->m_choffs);
}
// signal key on/off to our operators
void opl_emu_fm_channel_keyonoff(struct opl_emu_fm_channel* fmch,uint32_t states, enum opl_emu_keyon_type type, uint32_t chnum);
// prepare prior to clocking
int opl_emu_fm_channel_prepare(struct opl_emu_fm_channel* fmch);
// master clocking function
void opl_emu_fm_channel_clock(struct opl_emu_fm_channel* fmch,uint32_t env_counter, int32_t lfo_raw_pm);
// specific 2-operator and 4-operator output handlers
void opl_emu_fm_channel_output_2op(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax);
void opl_emu_fm_channel_output_4op(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax);
// compute the special OPL rhythm channel outputs
void opl_emu_fm_channel_output_rhythm_ch6(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax);
void opl_emu_fm_channel_output_rhythm_ch7(struct opl_emu_fm_channel* fmch,uint32_t phase_select,short *output, uint32_t rshift, int32_t clipmax);
void opl_emu_fm_channel_output_rhythm_ch8(struct opl_emu_fm_channel* fmch,uint32_t phase_select,short *output, uint32_t rshift, int32_t clipmax);
// are we a 4-operator channel or a 2-operator one?
int opl_emu_fm_channel_is4op( struct opl_emu_fm_channel* fmch )
{
return (fmch->m_op[2] != NULL);
}
// helper to add values to the outputs based on channel enables
void opl_emu_fm_channel_add_to_output(struct opl_emu_fm_channel* fmch,uint32_t choffs,short* output, int32_t value)
{
// create these constants to appease overzealous compilers checking array
// bounds in unreachable code (looking at you, clang)
int out0_index = 0;
int out1_index = 1 % OPL_EMU_REGISTERS_OUTPUTS;
int out2_index = 2 % OPL_EMU_REGISTERS_OUTPUTS;
int out3_index = 3 % OPL_EMU_REGISTERS_OUTPUTS;
if (OPL_EMU_REGISTERS_OUTPUTS == 1 || opl_emu_registers_ch_output_0(fmch->m_regs, choffs)) {
int s = output[out0_index] + value;
output[out0_index] = s < -32767 ? -32767 : s > 32767 ? 32767 : s;
}
if (OPL_EMU_REGISTERS_OUTPUTS >= 2 && opl_emu_registers_ch_output_1(fmch->m_regs, choffs)) {
int s = output[out1_index] + value;
output[out1_index] = s < -32767 ? -32767 : s > 32767 ? 32767 : s;
}
//if (OPL_EMU_REGISTERS_OUTPUTS >= 3 && opl_emu_registers_ch_output_2(fmch->m_regs, choffs))
// output->data[out2_index] += value;
//if (OPL_EMU_REGISTERS_OUTPUTS >= 4 && opl_emu_registers_ch_output_3(fmch->m_regs, choffs))
// output->data[out3_index] += value;
}
// ======================> ymf262
// ymf262 represents a set of operators and channels which together
// form a Yamaha FM core; chips that implement other engines (ADPCM, wavetable,
// etc) take this output and combine it with the others externally
struct opl_emu_t
{
uint32_t m_env_counter; // envelope counter; low 2 bits are sub-counter
uint8_t m_status; // current status register
uint8_t m_timer_running[2]; // current timer running state
uint32_t m_active_channels; // mask of active channels (computed by prepare)
uint32_t m_modified_channels; // mask of channels that have been modified
uint32_t m_prepare_count; // counter to do periodic prepare sweeps
struct opl_emu_registers m_regs; // register accessor
struct opl_emu_fm_channel m_channel[OPL_EMU_REGISTERS_CHANNELS]; // channel pointers
struct opl_emu_fm_operator m_operator[OPL_EMU_REGISTERS_OPERATORS]; // operator pointers
};
//*********************************************************
// YMF262
//*********************************************************
// set/reset bits in the status register, updating the IRQ status
uint8_t opl_emu_set_reset_status(struct opl_emu_t* emu, uint8_t set, uint8_t reset)
{
emu->m_status = (emu->m_status | set) & ~(reset | OPL_EMU_REGISTERS_STATUS_BUSY);
//m_intf.opl_emu_sync_check_interrupts();
return emu->m_status & ~opl_emu_registers_status_mask(&emu->m_regs);
}
void opl_emu_assign_operators( struct opl_emu_t* emu );
void opl_emu_write( struct opl_emu_t* emu, uint16_t regnum, uint8_t data);
//-------------------------------------------------
// ymf262 - constructor
//-------------------------------------------------
void opl_emu_init( struct opl_emu_t* emu )
{
emu->m_env_counter = 0;
emu->m_status = 0;
emu->m_timer_running[ 0 ] = 0;
emu->m_timer_running[ 1 ] = 0;
emu->m_active_channels = OPL_EMU_REGISTERS_ALL_CHANNELS;
emu->m_modified_channels = OPL_EMU_REGISTERS_ALL_CHANNELS;
emu->m_prepare_count = 0;
opl_emu_registers_init( &emu->m_regs );
// create the channels
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
opl_emu_fm_channel_init(&emu->m_channel[chnum], &emu->m_regs, opl_emu_registers_channel_offset(chnum));
// create the operators
for (uint32_t opnum = 0; opnum < OPL_EMU_REGISTERS_OPERATORS; opnum++)
opl_emu_fm_operator_init(&emu->m_operator[opnum],&emu->m_regs, opl_emu_registers_operator_offset(opnum));
// do the initial operator assignment
opl_emu_assign_operators( emu );
}
//-------------------------------------------------
// reset - reset the overall state
//-------------------------------------------------
void opl_emu_reset( struct opl_emu_t* emu)
{
// reset all status bits
opl_emu_set_reset_status(emu, 0, 0xff);
// register type-specific initialization
opl_emu_registers_reset( &emu->m_regs );
// explicitly write to the mode register since it has side-effects
opl_emu_write(emu, OPL_EMU_REGISTERS_REG_MODE, 0);
// reset the channels
for (int i = 0; i < sizeof( emu->m_channel ) / sizeof( *emu->m_channel ); ++i )
opl_emu_fm_channel_reset(&emu->m_channel[ i ]);
// reset the operators
for (int i = 0; i < sizeof( emu->m_operator ) / sizeof( *emu->m_operator ); ++i )
opl_emu_fm_operator_reset(&emu->m_operator[ i ]);
}
//-------------------------------------------------
// clock - iterate over all channels, clocking
// them forward one step
//-------------------------------------------------
uint32_t opl_emu_clock( struct opl_emu_t* emu,uint32_t chanmask)
{
// if something was modified, prepare
// also prepare every 4k samples to catch ending notes
if (emu->m_modified_channels != 0 || emu->m_prepare_count++ >= 4096)
{
// reassign operators to channels if dynamic
opl_emu_assign_operators(emu);
// call each channel to prepare
emu->m_active_channels = 0;
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
if (opl_emu_bitfield(chanmask, chnum,1))
if (opl_emu_fm_channel_prepare(&emu->m_channel[chnum]))
emu->m_active_channels |= 1 << chnum;
// reset the modified channels and prepare count
emu->m_modified_channels = emu->m_prepare_count = 0;
}
// if the envelope clock divider is 1, just increment by 4;
emu->m_env_counter += 4;
// clock the noise generator
int32_t lfo_raw_pm = opl_emu_registers_clock_noise_and_lfo(&emu->m_regs);
// now update the state of all the channels and operators
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
if (opl_emu_bitfield(chanmask, chnum, 1))
opl_emu_fm_channel_clock(&emu->m_channel[chnum], emu->m_env_counter, lfo_raw_pm);
// return the envelope counter as it is used to clock ADPCM-A
return emu->m_env_counter;
}
//-------------------------------------------------
// output - compute a sum over the relevant
// channels
//-------------------------------------------------
void opl_emu_out( struct opl_emu_t* emu, short* output, uint32_t rshift, int32_t clipmax, uint32_t chanmask)
{
// mask out inactive channels
chanmask &= emu->m_active_channels;
// handle the rhythm case, where some of the operators are dedicated
// to percussion (this is an OPL-specific feature)
if (opl_emu_registers_rhythm_enable(&emu->m_regs))
{
// precompute the operator 13+17 phase selection value
uint32_t op13phase = opl_emu_fm_operator_phase(&emu->m_operator[13]);
uint32_t op17phase = opl_emu_fm_operator_phase(&emu->m_operator[17]);
uint32_t phase_select = (opl_emu_bitfield(op13phase, 2, 1) ^ opl_emu_bitfield(op13phase, 7, 1)) | opl_emu_bitfield(op13phase, 3,1) | (opl_emu_bitfield(op17phase, 5,1) ^ opl_emu_bitfield(op17phase, 3,1));
// sum over all the desired channels
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
if (opl_emu_bitfield(chanmask, chnum,1))
{
if (chnum == 6)
opl_emu_fm_channel_output_rhythm_ch6(&emu->m_channel[chnum],output, rshift, clipmax);
else if (chnum == 7)
opl_emu_fm_channel_output_rhythm_ch7(&emu->m_channel[chnum],phase_select, output, rshift, clipmax);
else if (chnum == 8)
opl_emu_fm_channel_output_rhythm_ch8(&emu->m_channel[chnum],phase_select, output, rshift, clipmax);
else if (opl_emu_fm_channel_is4op(&emu->m_channel[chnum]))
opl_emu_fm_channel_output_4op(&emu->m_channel[chnum],output, rshift, clipmax);
else
opl_emu_fm_channel_output_2op(&emu->m_channel[chnum],output, rshift, clipmax);
}
}
else
{
// sum over all the desired channels
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
if (opl_emu_bitfield(chanmask, chnum,1))
{
if (opl_emu_fm_channel_is4op(&emu->m_channel[chnum]))
opl_emu_fm_channel_output_4op(&emu->m_channel[chnum],output, rshift, clipmax);
else
opl_emu_fm_channel_output_2op(&emu->m_channel[chnum],output, rshift, clipmax);
}
}
}
//-------------------------------------------------
// write - handle writes to the OPN registers
//-------------------------------------------------
void opl_emu_write( struct opl_emu_t* emu, uint16_t regnum, uint8_t data)
{
// special case: writes to the mode register can impact IRQs;
// schedule these writes to ensure ordering with timers
if (regnum == OPL_EMU_REGISTERS_REG_MODE)
{
// emu->m_intf.opl_emu_sync_mode_write(data);
return;
}
// for now just mark all channels as modified
emu->m_modified_channels = OPL_EMU_REGISTERS_ALL_CHANNELS;
// most writes are passive, consumed only when needed
uint32_t keyon_channel;
uint32_t keyon_opmask;
if (opl_emu_registers_write(&emu->m_regs,regnum, data, &keyon_channel, &keyon_opmask))
{
// handle writes to the keyon register(s)
if (keyon_channel < OPL_EMU_REGISTERS_CHANNELS)
{
// normal channel on/off
opl_emu_fm_channel_keyonoff(&emu->m_channel[keyon_channel],keyon_opmask, OPL_EMU_KEYON_NORMAL, keyon_channel);
}
else if (OPL_EMU_REGISTERS_CHANNELS >= 9 && keyon_channel == OPL_EMU_REGISTERS_RHYTHM_CHANNEL)
{
// special case for the OPL rhythm channels
opl_emu_fm_channel_keyonoff(&emu->m_channel[6],opl_emu_bitfield(keyon_opmask, 4,1) ? 3 : 0, OPL_EMU_KEYON_RHYTHM, 6);
opl_emu_fm_channel_keyonoff(&emu->m_channel[7],opl_emu_bitfield(keyon_opmask, 0,1) | (opl_emu_bitfield(keyon_opmask, 3,1) << 1), OPL_EMU_KEYON_RHYTHM, 7);
opl_emu_fm_channel_keyonoff(&emu->m_channel[8],opl_emu_bitfield(keyon_opmask, 2,1) | (opl_emu_bitfield(keyon_opmask, 1,1) << 1), OPL_EMU_KEYON_RHYTHM, 8);
}
}
}
//-------------------------------------------------
// assign_operators - get the current mapping of
// operators to channels and assign them all
//-------------------------------------------------
void opl_emu_assign_operators( struct opl_emu_t* emu)
{
struct opl_emu_registers_operator_mapping map;
opl_emu_registers_operator_map(&emu->m_regs, &map);
for (uint32_t chnum = 0; chnum < OPL_EMU_REGISTERS_CHANNELS; chnum++)
for (uint32_t index = 0; index < 4; index++)
{
uint32_t opnum = opl_emu_bitfield(map.chan[chnum], 8 * index, 8);
opl_emu_fm_channel_assign(&emu->m_channel[chnum],index, (opnum == 0xff) ? NULL : &emu->m_operator[opnum]);
}
}
//-------------------------------------------------
// update_timer - update the state of the given
// timer
//-------------------------------------------------
void opl_emu_update_timer( struct opl_emu_t* emu, uint32_t tnum, uint32_t enable)
{
// if the timer is live, but not currently enabled, set the timer
if (enable && !emu->m_timer_running[tnum])
{
// period comes from the registers, and is different for each
uint32_t period = (tnum == 0) ? (1024 - opl_emu_registers_timer_a_value(&emu->m_regs)) : 16 * (256 - opl_emu_registers_timer_b_value(&emu->m_regs));
// reset it
//emu->m_intf.opl_emu_set_timer(tnum, period * OPERATORS * emu->m_clock_prescale);
emu->m_timer_running[tnum] = 1;
}
// if the timer is not live, ensure it is not enabled
else if (!enable)
{
//emu->m_intf.opl_emu_set_timer(tnum, -1);
emu->m_timer_running[tnum] = 0;
}
}
//-------------------------------------------------
// generate - generate samples of sound
//-------------------------------------------------
void opl_emu_generate( struct opl_emu_t* emu,short *output, uint32_t numsamples, float volume )
{
volume = volume > 1.0f ? 1.0f : volume < 0.0f ? 0.0f : volume;
for (uint32_t samp = 0; samp < numsamples; samp++, output+=2)
{
// clock the system
opl_emu_clock(emu, OPL_EMU_REGISTERS_ALL_CHANNELS);
// update the FM content; mixing details for YMF262 need verification
opl_emu_out(emu, output, 0, 32767, OPL_EMU_REGISTERS_ALL_CHANNELS);
*output = (short)((*output) * volume);
*(output + 1) = (short)((*(output + 1)) * volume);
}
}
//-------------------------------------------------
// opl_emu_opl_key_scale_atten - converts an
// OPL concatenated block (3 bits) and fnum
// (10 bits) into an attenuation offset; values
// here are for 6dB/octave, in 0.75dB units
// (matching total level LSB)
//-------------------------------------------------
uint32_t opl_emu_opl_key_scale_atten(uint32_t block, uint32_t fnum_4msb)
{
// this table uses the top 4 bits of FNUM and are the maximal values
// (for when block == 7). Values for other blocks can be computed by
// subtracting 8 for each block below 7.
static uint8_t const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 };
int32_t result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7);
return opl_max(0, result);
}
//*********************************************************
// GLOBAL TABLE LOOKUPS
//*********************************************************
//-------------------------------------------------
// opl_emu_abs_sin_attenuation - given a sin (phase) input
// where the range 0-2*PI is mapped onto 10 bits,
// return the absolute value of sin(input),
// logarithmically-adjusted and treated as an
// attenuation value, in 4.8 fixed point format
//-------------------------------------------------
uint32_t opl_emu_abs_sin_attenuation(uint32_t input)
{
// the values here are stored as 4.8 logarithmic values for 1/4 phase
// this matches the internal format of the OPN chip, extracted from the die
static uint16_t const s_sin_table[256] =
{
0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365,
0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261,
0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd,
0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166,
0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118,
0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db,
0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9,
0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081,
0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060,
0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045,
0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f,
0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e,
0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011,
0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007,
0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002,
0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000
};
// if the top bit is set, we're in the second half of the curve
// which is a mirror image, so invert the index
if (opl_emu_bitfield(input, 8,1))
input = ~input;
// return the value from the table
return s_sin_table[input & 0xff];
}
//-------------------------------------------------
// opl_emu_attenuation_to_volume - given a 5.8 fixed point
// logarithmic attenuation value, return a 13-bit
// linear volume
//-------------------------------------------------
uint32_t opl_emu_attenuation_to_volume(uint32_t input)
{
// the values here are 10-bit mantissas with an implied leading bit
// this matches the internal format of the OPN chip, extracted from the die
// as a nod to performance, the implicit 0x400 bit is pre-incorporated, and
// the values are left-shifted by 2 so that a simple right shift is all that
// is needed; also the order is reversed to save a NOT on the input
#define X(a) (((a) | 0x400) << 2)
static uint16_t const s_power_table[256] =
{
X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4),
X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9),
X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f),
X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356),
X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e),
X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306),
X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0),
X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba),
X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295),
X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271),
X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e),
X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b),
X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209),
X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8),
X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8),
X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8),
X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189),
X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b),
X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d),
X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130),
X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114),
X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8),
X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc),
X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2),
X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8),
X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e),
X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075),
X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d),
X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045),
X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d),
X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016),
X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000)
};
#undef X
// look up the fractional part, then shift by the whole
return s_power_table[input & 0xff] >> (input >> 8);
}
//-------------------------------------------------
// opl_emu_attenuation_increment - given a 6-bit ADSR
// rate value and a 3-bit stepping index,
// return a 4-bit increment to the attenutaion
// for this step (or for the attack case, the
// fractional scale factor to decrease by)
//-------------------------------------------------
uint32_t opl_emu_attenuation_increment(uint32_t rate, uint32_t index)
{
static uint32_t const s_increment_table[64] =
{
0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03)
0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B)
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F)
0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33)
0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37)
0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B)
0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F)
};
return opl_emu_bitfield(s_increment_table[rate], 4*index, 4);
}
//*********************************************************
// OPL REGISTERS
//*********************************************************
//-------------------------------------------------
// opl_emu_registers - constructor
//-------------------------------------------------
void opl_emu_registers_init(struct opl_emu_registers* regs)
{
regs->m_lfo_am_counter = 0;
regs->m_lfo_pm_counter = 0;
regs->m_noise_lfsr = 1;
regs->m_lfo_am = 0;
// create these pointers to appease overzealous compilers checking array
// bounds in unreachable code (looking at you, clang)
uint16_t *wf0 = ®s->m_waveform[0][0];
uint16_t *wf1 = ®s->m_waveform[1 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf2 = ®s->m_waveform[2 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf3 = ®s->m_waveform[3 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf4 = ®s->m_waveform[4 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf5 = ®s->m_waveform[5 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf6 = ®s->m_waveform[6 % OPL_EMU_REGISTERS_WAVEFORMS][0];
uint16_t *wf7 = ®s->m_waveform[7 % OPL_EMU_REGISTERS_WAVEFORMS][0];
// create the waveforms
for (uint32_t index = 0; index < OPL_EMU_REGISTERS_WAVEFORM_LENGTH; index++)
wf0[index] = opl_emu_abs_sin_attenuation(index) | (opl_emu_bitfield(index, 9,1) << 15);