/* ------------------------------------------------------------------------------ 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 #include #include /* calloc() */ #include /* 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); if (OPL_EMU_REGISTERS_WAVEFORMS >= 4) { uint16_t zeroval = wf0[0]; for (uint32_t index = 0; index < OPL_EMU_REGISTERS_WAVEFORM_LENGTH; index++) { wf1[index] = opl_emu_bitfield(index, 9,1) ? zeroval : wf0[index]; wf2[index] = wf0[index] & 0x7fff; wf3[index] = opl_emu_bitfield(index, 8,1) ? zeroval : (wf0[index] & 0x7fff); if (OPL_EMU_REGISTERS_WAVEFORMS >= 8) { wf4[index] = opl_emu_bitfield(index, 9,1) ? zeroval : wf0[index * 2]; wf5[index] = opl_emu_bitfield(index, 9,1) ? zeroval : wf0[(index * 2) & 0x1ff]; wf6[index] = opl_emu_bitfield(index, 9,1) << 15; wf7[index] = (opl_emu_bitfield(index, 9,1) ? (index ^ 0x13ff) : index) << 3; } } } } //------------------------------------------------- // reset - reset to initial state //------------------------------------------------- void opl_emu_registers_reset(struct opl_emu_registers* regs) { for( int i = 0; i < OPL_EMU_REGISTERS_REGISTERS; ++i ) regs->m_regdata[ i ] = 0; } //------------------------------------------------- // opl_emu_registers_operator_map - return an array of operator // indices for each channel; for OPL this is fixed //------------------------------------------------- void opl_emu_registers_operator_map(struct opl_emu_registers* regs, struct opl_emu_registers_operator_mapping* dest) { // OPL3/OPL4 can be configured for 2 or 4 operators uint32_t fourop = opl_emu_registers_fourop_enable(regs); dest->chan[ 0] = opl_emu_bitfield(fourop, 0,1) ? opl_emu_registers_operator_list( 0, 3, 6, 9 ) : opl_emu_registers_operator_list( 0, 3, 0xff, 0xff ); dest->chan[ 1] = opl_emu_bitfield(fourop, 1,1) ? opl_emu_registers_operator_list( 1, 4, 7, 10 ) : opl_emu_registers_operator_list( 1, 4, 0xff, 0xff ); dest->chan[ 2] = opl_emu_bitfield(fourop, 2,1) ? opl_emu_registers_operator_list( 2, 5, 8, 11 ) : opl_emu_registers_operator_list( 2, 5, 0xff, 0xff ); dest->chan[ 3] = opl_emu_bitfield(fourop, 0,1) ? opl_emu_registers_operator_list( 0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 6, 9, 0xff, 0xff ); dest->chan[ 4] = opl_emu_bitfield(fourop, 1,1) ? opl_emu_registers_operator_list( 0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 7, 10, 0xff, 0xff ); dest->chan[ 5] = opl_emu_bitfield(fourop, 2,1) ? opl_emu_registers_operator_list( 0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 8, 11, 0xff, 0xff ); dest->chan[ 6] = opl_emu_registers_operator_list( 12, 15, 0xff, 0xff ); dest->chan[ 7] = opl_emu_registers_operator_list( 13, 16, 0xff, 0xff ); dest->chan[ 8] = opl_emu_registers_operator_list( 14, 17, 0xff, 0xff ); dest->chan[ 9] = opl_emu_bitfield(fourop, 3,1) ? opl_emu_registers_operator_list( 18, 21, 24, 27 ) : opl_emu_registers_operator_list( 18, 21, 0xff, 0xff ); dest->chan[10] = opl_emu_bitfield(fourop, 4,1) ? opl_emu_registers_operator_list( 19, 22, 25, 28 ) : opl_emu_registers_operator_list( 19, 22, 0xff, 0xff ); dest->chan[11] = opl_emu_bitfield(fourop, 5,1) ? opl_emu_registers_operator_list( 20, 23, 26, 29 ) : opl_emu_registers_operator_list( 20, 23, 0xff, 0xff ); dest->chan[12] = opl_emu_bitfield(fourop, 3,1) ? opl_emu_registers_operator_list(0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 24, 27, 0xff, 0xff ); dest->chan[13] = opl_emu_bitfield(fourop, 4,1) ? opl_emu_registers_operator_list(0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 25, 28, 0xff, 0xff ); dest->chan[14] = opl_emu_bitfield(fourop, 5,1) ? opl_emu_registers_operator_list(0xff, 0xff, 0xff, 0xff) : opl_emu_registers_operator_list( 26, 29, 0xff, 0xff ); dest->chan[15] = opl_emu_registers_operator_list( 30, 33, 0xff, 0xff ); dest->chan[16] = opl_emu_registers_operator_list( 31, 34, 0xff, 0xff ); dest->chan[17] = opl_emu_registers_operator_list( 32, 35, 0xff, 0xff ); } //------------------------------------------------- // write - handle writes to the register array //------------------------------------------------- int opl_emu_registers_write(struct opl_emu_registers* regs,uint16_t index, uint8_t data, uint32_t *channel, uint32_t *opmask) { // writes to the mode register with high bit set ignore the low bits if (index == OPL_EMU_REGISTERS_REG_MODE && opl_emu_bitfield(data, 7,1) != 0) regs->m_regdata[index] |= 0x80; else regs->m_regdata[index] = data; // handle writes to the rhythm keyons if (index == 0xbd) { *channel = OPL_EMU_REGISTERS_RHYTHM_CHANNEL; *opmask = opl_emu_bitfield(data, 5,1) ? opl_emu_bitfield(data, 0, 5) : 0; return 1; } // handle writes to the channel keyons if ((index & 0xf0) == 0xb0) { *channel = index & 0x0f; if (*channel < 9) { *channel += 9 * opl_emu_bitfield(index, 8,1); *opmask = opl_emu_bitfield(data, 5,1) ? 15 : 0; return 1; } } return 0; } //------------------------------------------------- // clock_noise_and_lfo - clock the noise and LFO, // handling clock division, depth, and waveform // computations //------------------------------------------------- static int32_t opl_emu_opl_clock_noise_and_lfo(uint32_t *noise_lfsr, uint16_t *lfo_am_counter, uint16_t *lfo_pm_counter, uint8_t *lfo_am, uint32_t am_depth, uint32_t pm_depth) { // OPL has a 23-bit noise generator for the rhythm section, running at // a constant rate, used only for percussion input *noise_lfsr <<= 1; *noise_lfsr |= opl_emu_bitfield(*noise_lfsr, 23,1) ^ opl_emu_bitfield(*noise_lfsr, 9,1) ^ opl_emu_bitfield(*noise_lfsr, 8,1) ^ opl_emu_bitfield(*noise_lfsr, 1,1); // OPL has two fixed-frequency LFOs, one for AM, one for PM // the AM LFO has 210*64 steps; at a nominal 50kHz output, // this equates to a period of 50000/(210*64) = 3.72Hz uint32_t am_counter = *lfo_am_counter++; if (am_counter >= 210*64 - 1) *lfo_am_counter = 0; // low 8 bits are fractional; depth 0 is divided by 2, while depth 1 is times 2 int shift = 9 - 2 * am_depth; // AM value is the upper bits of the value, inverted across the midpoint // to produce a triangle *lfo_am = ((am_counter < 105*64) ? am_counter : (210*64+63 - am_counter)) >> shift; // the PM LFO has 8192 steps, or a nominal period of 6.1Hz uint32_t pm_counter = *lfo_pm_counter++; // PM LFO is broken into 8 chunks, each lasting 1024 steps; the PM value // depends on the upper bits of FNUM, so this value is a fraction and // sign to apply to that value, as a 1.3 value static int8_t pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 }; return pm_scale[opl_emu_bitfield(pm_counter, 10, 3)] >> (pm_depth ^ 1); } int32_t opl_emu_registers_clock_noise_and_lfo(struct opl_emu_registers* regs) { return opl_emu_opl_clock_noise_and_lfo(®s->m_noise_lfsr, ®s->m_lfo_am_counter, ®s->m_lfo_pm_counter, ®s->m_lfo_am, opl_emu_registers_lfo_am_depth(regs), opl_emu_registers_lfo_pm_depth(regs)); } //------------------------------------------------- // cache_operator_data - fill the operator cache // with prefetched data; note that this code is // also used by ymopna_registers, so it must // handle upper channels cleanly //------------------------------------------------- void opl_emu_registers_cache_operator_data(struct opl_emu_registers* regs,uint32_t choffs, uint32_t opoffs, struct opl_emu_opdata_cache *cache) { // set up the easy stuff cache->waveform = ®s->m_waveform[opl_emu_registers_op_waveform(regs, opoffs) % OPL_EMU_REGISTERS_WAVEFORMS][0]; // get frequency from the channel uint32_t block_freq = cache->block_freq = opl_emu_registers_ch_block_freq(regs,choffs); // compute the keycode: block_freq is: // // 111 | // 21098|76543210 // BBBFF|FFFFFFFF // ^^^?? // // the 4-bit keycode uses the top 3 bits plus one of the next two bits uint32_t keycode = opl_emu_bitfield(block_freq, 10, 3) << 1; // lowest bit is determined by note_select(); note that it is // actually reversed from what the manual says, however keycode |= opl_emu_bitfield(block_freq, 9 - opl_emu_registers_note_select(regs), 1); // no detune adjustment on OPL cache->detune = 0; // multiple value, as an x.1 value (0 means 0.5) // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 uint32_t multiple = opl_emu_registers_op_multiple(regs,opoffs); cache->multiple = ((multiple & 0xe) | opl_emu_bitfield(0xc2aa, multiple,1)) * 2; if (cache->multiple == 0) cache->multiple = 1; // phase step, or OPL_EMU_PHASE_STEP_DYNAMIC if PM is active; this depends on block_freq, detune, // and multiple, so compute it after we've done those if (opl_emu_registers_op_lfo_pm_enable(regs,opoffs) == 0) cache->phase_step = opl_emu_registers_compute_phase_step(regs,choffs, opoffs, cache, 0); else cache->phase_step = OPL_EMU_PHASE_STEP_DYNAMIC; // total level, scaled by 8 cache->total_level = opl_emu_registers_op_total_level(regs,opoffs) << 3; // pre-add key scale level uint32_t ksl = opl_emu_registers_op_ksl(regs,opoffs); if (ksl != 0) cache->total_level += opl_emu_opl_key_scale_atten(opl_emu_bitfield(block_freq, 10, 3), opl_emu_bitfield(block_freq, 6, 4)) << ksl; // 4-bit sustain level, but 15 means 31 so effectively 5 bits cache->eg_sustain = opl_emu_registers_op_sustain_level(regs,opoffs); cache->eg_sustain |= (cache->eg_sustain + 1) & 0x10; cache->eg_sustain <<= 5; // determine KSR adjustment for enevlope rates uint32_t ksrval = keycode >> (2 * (opl_emu_registers_op_ksr(regs,opoffs) ^ 1)); cache->eg_rate[OPL_EMU_EG_ATTACK] = opl_emu_registers_effective_rate(opl_emu_registers_op_attack_rate(regs,opoffs) * 4, ksrval); cache->eg_rate[OPL_EMU_EG_DECAY] = opl_emu_registers_effective_rate(opl_emu_registers_op_decay_rate(regs,opoffs) * 4, ksrval); cache->eg_rate[OPL_EMU_EG_SUSTAIN] = opl_emu_registers_op_eg_sustain(regs,opoffs) ? 0 : opl_emu_registers_effective_rate(opl_emu_registers_op_release_rate(regs,opoffs) * 4, ksrval); cache->eg_rate[OPL_EMU_EG_RELEASE] = opl_emu_registers_effective_rate(opl_emu_registers_op_release_rate(regs,opoffs) * 4, ksrval); } //------------------------------------------------- // compute_phase_step - compute the phase step //------------------------------------------------- static uint32_t opl_emu_opl_compute_phase_step(uint32_t block_freq, uint32_t multiple, int32_t lfo_raw_pm) { // OPL phase calculation has no detuning, but uses FNUMs like // the OPN version, and computes PM a bit differently // extract frequency number as a 12-bit fraction uint32_t fnum = opl_emu_bitfield(block_freq, 0, 10) << 2; // apply the phase adjustment based on the upper 3 bits // of FNUM and the PM depth parameters fnum += (lfo_raw_pm * opl_emu_bitfield(block_freq, 7, 3)) >> 1; // keep fnum to 12 bits fnum &= 0xfff; // apply block shift to compute phase step uint32_t block = opl_emu_bitfield(block_freq, 10, 3); uint32_t phase_step = (fnum << block) >> 2; // apply frequency multiplier (which is cached as an x.1 value) return (phase_step * multiple) >> 1; } 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 opl_emu_opl_compute_phase_step(cache->block_freq, cache->multiple, opl_emu_registers_op_lfo_pm_enable(regs,opoffs) ? lfo_raw_pm : 0); } //********************************************************* // FM OPERATOR //********************************************************* //------------------------------------------------- // opl_emu_fm_operator - constructor //------------------------------------------------- void opl_emu_fm_operator_init(struct opl_emu_fm_operator* fmop, struct opl_emu_registers* regs, uint32_t opoffs) { fmop->m_choffs = 0; fmop->m_opoffs = opoffs; fmop->m_phase = 0; fmop->m_env_attenuation = 0x3ff; fmop->m_env_state = OPL_EMU_EG_RELEASE; fmop->m_key_state = 0; fmop->m_keyon_live = 0; fmop->m_regs = regs; fmop->m_cache.eg_shift = 0; } //------------------------------------------------- // reset - reset the channel state //------------------------------------------------- void opl_emu_fm_operator_reset(struct opl_emu_fm_operator* fmop) { // reset our data fmop->m_phase = 0; fmop->m_env_attenuation = 0x3ff; fmop->m_env_state = OPL_EMU_EG_RELEASE; fmop->m_key_state = 0; fmop->m_keyon_live = 0; } //------------------------------------------------- // prepare - prepare for clocking //------------------------------------------------- int opl_emu_fm_operator_prepare(struct opl_emu_fm_operator* fmop) { // cache the data opl_emu_registers_cache_operator_data(fmop->m_regs,fmop->m_choffs, fmop->m_opoffs, &fmop->m_cache); // clock the key state opl_emu_fm_operator_clock_keystate(fmop,(uint32_t)(fmop->m_keyon_live != 0)); fmop->m_keyon_live &= ~(1 << OPL_EMU_KEYON_CSM); // we're active until we're quiet after the release return (fmop->m_env_state != OPL_EMU_EG_RELEASE || fmop->m_env_attenuation < OPL_EMU_FM_OPERATOR_EG_QUIET); } //------------------------------------------------- // clock - master clocking function //------------------------------------------------- void opl_emu_fm_operator_clock(struct opl_emu_fm_operator* fmop, uint32_t env_counter, int32_t lfo_raw_pm) { // clock the envelope if on an envelope cycle; env_counter is a x.2 value if (opl_emu_bitfield(env_counter, 0, 2) == 0) opl_emu_fm_operator_clock_envelope(fmop,env_counter >> 2); // clock the phase opl_emu_fm_operator_clock_phase(fmop,lfo_raw_pm); } //------------------------------------------------- // compute_volume - compute the 14-bit signed // volume of this operator, given a phase // modulation and an AM LFO offset //------------------------------------------------- int32_t opl_emu_fm_operator_compute_volume(struct opl_emu_fm_operator* fmop, uint32_t phase, uint32_t am_offset) { // the low 10 bits of phase represents a full 2*PI period over // the full sin wave // early out if the envelope is effectively off if (fmop->m_env_attenuation > OPL_EMU_FM_OPERATOR_EG_QUIET) return 0; // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value uint32_t sin_attenuation = fmop->m_cache.waveform[phase & (OPL_EMU_REGISTERS_WAVEFORM_LENGTH - 1)]; // get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8 uint32_t env_attenuation = opl_emu_fm_operator_envelope_attenuation(fmop, am_offset) << 2; // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume int32_t result = opl_emu_attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation); // negate if in the negative part of the sin wave (sign bit gives 14 bits) return opl_emu_bitfield(sin_attenuation, 15,1) ? -result : result; } //------------------------------------------------- // keyonoff - signal a key on/off event //------------------------------------------------- void opl_emu_fm_operator_keyonoff(struct opl_emu_fm_operator* fmop, uint32_t on, enum opl_emu_keyon_type type) { fmop->m_keyon_live = (fmop->m_keyon_live & ~(1 << (int)(type))) | (opl_emu_bitfield(on, 0,1) << (int)(type)); } //------------------------------------------------- // start_attack - start the attack phase; called // when a keyon happens //------------------------------------------------- void opl_emu_fm_operator_start_attack(struct opl_emu_fm_operator* fmop) { // don't change anything if already in attack state if (fmop->m_env_state == OPL_EMU_EG_ATTACK) return; fmop->m_env_state = OPL_EMU_EG_ATTACK; // reset the phase when we start an attack due to a key on fmop->m_phase = 0; // if the attack rate >= 62 then immediately go to max attenuation if (fmop->m_cache.eg_rate[OPL_EMU_EG_ATTACK] >= 62) fmop->m_env_attenuation = 0; } //------------------------------------------------- // start_release - start the release phase; // called when a keyoff happens //------------------------------------------------- void opl_emu_fm_operator_start_release(struct opl_emu_fm_operator* fmop) { // don't change anything if already in release state if (fmop->m_env_state >= OPL_EMU_EG_RELEASE) return; fmop->m_env_state = OPL_EMU_EG_RELEASE; } //------------------------------------------------- // clock_keystate - clock the keystate to match // the incoming keystate //------------------------------------------------- void opl_emu_fm_operator_clock_keystate(struct opl_emu_fm_operator* fmop, uint32_t keystate) { // has the key changed? if ((keystate ^ fmop->m_key_state) != 0) { fmop->m_key_state = keystate; // if the key has turned on, start the attack if (keystate != 0) { opl_emu_fm_operator_start_attack(fmop); } // otherwise, start the release else opl_emu_fm_operator_start_release(fmop); } } //------------------------------------------------- // clock_envelope - clock the envelope state // according to the given count //------------------------------------------------- void opl_emu_fm_operator_clock_envelope(struct opl_emu_fm_operator* fmop, uint32_t env_counter) { // handle attack->decay transitions if (fmop->m_env_state == OPL_EMU_EG_ATTACK && fmop->m_env_attenuation == 0) fmop->m_env_state = OPL_EMU_EG_DECAY; // handle decay->sustain transitions; it is important to do this immediately // after the attack->decay transition above in the event that the sustain level // is set to 0 (in which case we will skip right to sustain without doing any // decay); as an example where this can be heard, check the cymbals sound // in channel 0 of shinobi's test mode sound #5 if (fmop->m_env_state == OPL_EMU_EG_DECAY && fmop->m_env_attenuation >= fmop->m_cache.eg_sustain) fmop->m_env_state = OPL_EMU_EG_SUSTAIN; // fetch the appropriate 6-bit rate value from the cache uint32_t rate = fmop->m_cache.eg_rate[fmop->m_env_state]; // compute the rate shift value; this is the shift needed to // apply to the env_counter such that it becomes a 5.11 fixed // point number uint32_t rate_shift = rate >> 2; env_counter <<= rate_shift; // see if the fractional part is 0; if not, it's not time to clock if (opl_emu_bitfield(env_counter, 0, 11) != 0) return; // determine the increment based on the non-fractional part of env_counter uint32_t relevant_bits = opl_emu_bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); uint32_t increment = opl_emu_attenuation_increment(rate, relevant_bits); // attack is the only one that increases if (fmop->m_env_state == OPL_EMU_EG_ATTACK) { // glitch means that attack rates of 62/63 don't increment if // changed after the initial key on (where they are handled // specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL // at least so assuming it is true for everyone if (rate < 62) fmop->m_env_attenuation += (~fmop->m_env_attenuation * increment) >> 4; } // all other cases are similar else { // non-SSG-EG cases just apply the increment fmop->m_env_attenuation += increment; // clamp the final attenuation if (fmop->m_env_attenuation >= 0x400) fmop->m_env_attenuation = 0x3ff; } } //------------------------------------------------- // clock_phase - clock the 10.10 phase value; the // OPN version of the logic has been verified // against the Nuked phase generator //------------------------------------------------- void opl_emu_fm_operator_clock_phase(struct opl_emu_fm_operator* fmop, int32_t lfo_raw_pm) { // read from the cache, or recalculate if PM active uint32_t phase_step = fmop->m_cache.phase_step; if (phase_step == OPL_EMU_PHASE_STEP_DYNAMIC) phase_step = opl_emu_registers_compute_phase_step(fmop->m_regs,fmop->m_choffs, fmop->m_opoffs, &fmop->m_cache, lfo_raw_pm); // finally apply the step to the current phase value fmop->m_phase += phase_step; } //------------------------------------------------- // envelope_attenuation - return the effective // attenuation of the envelope //------------------------------------------------- uint32_t opl_emu_fm_operator_envelope_attenuation(struct opl_emu_fm_operator* fmop, uint32_t am_offset) { uint32_t result = fmop->m_env_attenuation >> fmop->m_cache.eg_shift; // add in LFO AM modulation if (opl_emu_registers_op_lfo_am_enable(fmop->m_regs,fmop->m_opoffs)) result += am_offset; // add in total level and KSL from the cache result += fmop->m_cache.total_level; // clamp to max, apply shift, and return return opl_min(result, 0x3ff); } //********************************************************* // FM CHANNEL //********************************************************* //------------------------------------------------- // opl_emu_fm_channel - constructor //------------------------------------------------- void opl_emu_fm_channel_init(struct opl_emu_fm_channel* fmch,struct opl_emu_registers* regs, uint32_t choffs) { fmch->m_choffs = choffs; fmch->m_feedback[ 0 ] = 0; fmch->m_feedback[ 1 ] = 0; fmch->m_feedback_in = 0; fmch->m_op[ 0 ] = NULL; fmch->m_op[ 1 ] = NULL; fmch->m_op[ 2 ] = NULL; fmch->m_op[ 3 ] = NULL; fmch->m_regs = regs; } //------------------------------------------------- // reset - reset the channel state //------------------------------------------------- void opl_emu_fm_channel_reset(struct opl_emu_fm_channel* fmch) { // reset our data fmch->m_feedback[0] = fmch->m_feedback[1] = 0; fmch->m_feedback_in = 0; } //------------------------------------------------- // keyonoff - 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) { for (uint32_t opnum = 0; opnum < sizeof( fmch->m_op ) / sizeof( *fmch->m_op ); opnum++) if (fmch->m_op[opnum] != NULL) opl_emu_fm_operator_keyonoff(fmch->m_op[opnum],opl_emu_bitfield(states, opnum,1), type); } //------------------------------------------------- // prepare - prepare for clocking //------------------------------------------------- int opl_emu_fm_channel_prepare(struct opl_emu_fm_channel* fmch) { uint32_t active_mask = 0; // prepare all operators and determine if they are active for (uint32_t opnum = 0; opnum < sizeof( fmch->m_op ) / sizeof( *fmch->m_op ); opnum++) if (fmch->m_op[opnum] != NULL) if (opl_emu_fm_operator_prepare(fmch->m_op[opnum])) active_mask |= 1 << opnum; return (active_mask != 0); } //------------------------------------------------- // clock - master clock of all operators //------------------------------------------------- void opl_emu_fm_channel_clock(struct opl_emu_fm_channel* fmch,uint32_t env_counter, int32_t lfo_raw_pm) { // clock the feedback through fmch->m_feedback[0] = fmch->m_feedback[1]; fmch->m_feedback[1] = fmch->m_feedback_in; for (uint32_t opnum = 0; opnum < sizeof( fmch->m_op ) / sizeof( *fmch->m_op ); opnum++) if (fmch->m_op[opnum] != NULL) opl_emu_fm_operator_clock(fmch->m_op[opnum],env_counter, lfo_raw_pm); } //------------------------------------------------- // output_2op - combine 4 operators according to // the specified algorithm, returning a sum // according to the rshift and clipmax parameters, // which vary between different implementations //------------------------------------------------- void opl_emu_fm_channel_output_2op(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax) { // The first 2 operators should be populated // AM amount is the same across all operators; compute it once uint32_t am_offset = opl_emu_registers_lfo_am_offset(fmch->m_regs,fmch->m_choffs); // operator 1 has optional self-feedback int32_t opmod = 0; uint32_t feedback = opl_emu_registers_ch_feedback(fmch->m_regs,fmch->m_choffs); if (feedback != 0) opmod = (fmch->m_feedback[0] + fmch->m_feedback[1]) >> (10 - feedback); // compute the 14-bit volume/value of operator 1 and update the feedback int32_t op1value = fmch->m_feedback_in = opl_emu_fm_operator_compute_volume(fmch->m_op[0], opl_emu_fm_operator_phase(fmch->m_op[0]) + opmod, am_offset); // now that the feedback has been computed, skip the rest if all volumes // are clear; no need to do all this work for nothing if (opl_emu_registers_ch_output_any(fmch->m_regs,fmch->m_choffs) == 0) return; // Algorithms for two-operator case: // 0: O1 -> O2 -> out // 1: (O1 + O2) -> out int32_t result; if (opl_emu_bitfield(opl_emu_registers_ch_algorithm(fmch->m_regs,fmch->m_choffs), 0,1) == 0) { // some OPL chips use the previous sample for modulation instead of // the current sample opmod = op1value >> 1; result = opl_emu_fm_operator_compute_volume(fmch->m_op[1], opl_emu_fm_operator_phase(fmch->m_op[1]) + opmod, am_offset) >> rshift; } else { result = op1value + (opl_emu_fm_operator_compute_volume(fmch->m_op[1], opl_emu_fm_operator_phase(fmch->m_op[1]), am_offset) >> rshift); int32_t clipmin = -clipmax - 1; result = opl_emu_clamp(result, clipmin, clipmax); } // add to the output opl_emu_fm_channel_add_to_output(fmch,fmch->m_choffs, output, result); } //------------------------------------------------- // output_4op - combine 4 operators according to // the specified algorithm, returning a sum // according to the rshift and clipmax parameters, // which vary between different implementations //------------------------------------------------- void opl_emu_fm_channel_output_4op(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax) { // AM amount is the same across all operators; compute it once uint32_t am_offset = opl_emu_registers_lfo_am_offset(fmch->m_regs,fmch->m_choffs); // operator 1 has optional self-feedback int32_t opmod = 0; uint32_t feedback = opl_emu_registers_ch_feedback(fmch->m_regs,fmch->m_choffs); if (feedback != 0) opmod = (fmch->m_feedback[0] + fmch->m_feedback[1]) >> (10 - feedback); // compute the 14-bit volume/value of operator 1 and update the feedback int32_t op1value = fmch->m_feedback_in = opl_emu_fm_operator_compute_volume(fmch->m_op[0], opl_emu_fm_operator_phase(fmch->m_op[0]) + opmod, am_offset); // now that the feedback has been computed, skip the rest if all volumes // are clear; no need to do all this work for nothing if (opl_emu_registers_ch_output_any(fmch->m_regs,fmch->m_choffs) == 0) return; // OPM/OPN offer 8 different connection algorithms for 4 operators, // and OPL3 offers 4 more, which we designate here as 8-11. // // The operators are computed in order, with the inputs pulled from // an array of values (opout) that is populated as we go: // 0 = 0 // 1 = O1 // 2 = O2 // 3 = O3 // 4 = (O4) // 5 = O1+O2 // 6 = O1+O3 // 7 = O2+O3 // // The s_algorithm_ops table describes the inputs and outputs of each // algorithm as follows: // // ---------x use opout[x] as operator 2 input // ------xxx- use opout[x] as operator 3 input // ---xxx---- use opout[x] as operator 4 input // --x------- include opout[1] in final sum // -x-------- include opout[2] in final sum // x--------- include opout[3] in final sum #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \ ((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9)) static uint16_t const s_algorithm_ops[8+4] = { ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4) ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4) ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4) ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4) ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4) ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4) ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4) ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0] ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique] ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4] ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique] }; uint32_t algorithm_ops = s_algorithm_ops[opl_emu_registers_ch_algorithm(fmch->m_regs,fmch->m_choffs)]; // populate the opout table int16_t opout[8]; opout[0] = 0; opout[1] = op1value; // compute the 14-bit volume/value of operator 2 opmod = opout[opl_emu_bitfield(algorithm_ops, 0, 1)] >> 1; opout[2] = opl_emu_fm_operator_compute_volume(fmch->m_op[1], opl_emu_fm_operator_phase(fmch->m_op[1]) + opmod, am_offset); opout[5] = opout[1] + opout[2]; // compute the 14-bit volume/value of operator 3 opmod = opout[opl_emu_bitfield(algorithm_ops, 1, 3)] >> 1; opout[3] = opl_emu_fm_operator_compute_volume(fmch->m_op[2], opl_emu_fm_operator_phase(fmch->m_op[2]) + opmod, am_offset); opout[6] = opout[1] + opout[3]; opout[7] = opout[2] + opout[3]; // compute the 14-bit volume/value of operator 4; this could be a noise // value on the OPM; all algorithms consume OP4 output at a minimum opmod = opout[opl_emu_bitfield(algorithm_ops, 4, 3)] >> 1; int32_t result = opl_emu_fm_operator_compute_volume(fmch->m_op[3], opl_emu_fm_operator_phase(fmch->m_op[3]) + opmod, am_offset); result >>= rshift; // optionally add OP1, OP2, OP3 int32_t clipmin = -clipmax - 1; if (opl_emu_bitfield(algorithm_ops, 7,1) != 0) result = opl_emu_clamp(result + (opout[1] >> rshift), clipmin, clipmax); if (opl_emu_bitfield(algorithm_ops, 8,1) != 0) result = opl_emu_clamp(result + (opout[2] >> rshift), clipmin, clipmax); if (opl_emu_bitfield(algorithm_ops, 9,1) != 0) result = opl_emu_clamp(result + (opout[3] >> rshift), clipmin, clipmax); // add to the output opl_emu_fm_channel_add_to_output(fmch,fmch->m_choffs, output, result); } //------------------------------------------------- // output_rhythm_ch6 - special case output // computation for OPL channel 6 in rhythm mode, // which outputs a Bass Drum instrument //------------------------------------------------- void opl_emu_fm_channel_output_rhythm_ch6(struct opl_emu_fm_channel* fmch,short *output, uint32_t rshift, int32_t clipmax) { // AM amount is the same across all operators; compute it once uint32_t am_offset = opl_emu_registers_lfo_am_offset(fmch->m_regs,fmch->m_choffs); // Bass Drum: this uses operators 12 and 15 (i.e., channel 6) // in an almost-normal way, except that if the algorithm is 1, // the first operator is ignored instead of added in // operator 1 has optional self-feedback int32_t opmod = 0; uint32_t feedback = opl_emu_registers_ch_feedback(fmch->m_regs,fmch->m_choffs); if (feedback != 0) opmod = (fmch->m_feedback[0] + fmch->m_feedback[1]) >> (10 - feedback); // compute the 14-bit volume/value of operator 1 and update the feedback int32_t opout1 = fmch->m_feedback_in = opl_emu_fm_operator_compute_volume(fmch->m_op[0], opl_emu_fm_operator_phase(fmch->m_op[0]) + opmod, am_offset); // compute the 14-bit volume/value of operator 2, which is the result opmod = opl_emu_bitfield(opl_emu_registers_ch_algorithm(fmch->m_regs,fmch->m_choffs), 0,1) ? 0 : (opout1 >> 1); int32_t result = opl_emu_fm_operator_compute_volume(fmch->m_op[1], opl_emu_fm_operator_phase(fmch->m_op[1]) + opmod, am_offset) >> rshift; // add to the output opl_emu_fm_channel_add_to_output(fmch, fmch->m_choffs, output, result * 2); } //------------------------------------------------- // output_rhythm_ch7 - special case output // computation for OPL channel 7 in rhythm mode, // which outputs High Hat and Snare Drum // instruments //------------------------------------------------- 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) { // AM amount is the same across all operators; compute it once uint32_t am_offset = opl_emu_registers_lfo_am_offset(fmch->m_regs,fmch->m_choffs); uint32_t noise_state = opl_emu_bitfield(opl_emu_registers_noise_state(fmch->m_regs), 0,1); // High Hat: this uses the envelope from operator 13 (channel 7), // and a combination of noise and the operator 13/17 phase select // to compute the phase uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); int32_t result = opl_emu_fm_operator_compute_volume(fmch->m_op[0], phase, am_offset) >> rshift; // Snare Drum: this uses the envelope from operator 16 (channel 7), // and a combination of noise and operator 13 phase to pick a phase uint32_t op13phase = opl_emu_fm_operator_phase(fmch->m_op[0]); phase = (0x100 << opl_emu_bitfield(op13phase, 8,1)) ^ (noise_state << 8); result += opl_emu_fm_operator_compute_volume(fmch->m_op[1],phase, am_offset) >> rshift; result = opl_emu_clamp(result, -clipmax - 1, clipmax); // add to the output opl_emu_fm_channel_add_to_output(fmch, fmch->m_choffs, output, result * 2); } //------------------------------------------------- // output_rhythm_ch8 - special case output // computation for OPL channel 8 in rhythm mode, // which outputs Tom Tom and Top Cymbal instruments //------------------------------------------------- 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) { // AM amount is the same across all operators; compute it once uint32_t am_offset = opl_emu_registers_lfo_am_offset(fmch->m_regs,fmch->m_choffs); // Tom Tom: this is just a single operator processed normally int32_t result = opl_emu_fm_operator_compute_volume(fmch->m_op[0], opl_emu_fm_operator_phase(fmch->m_op[0]), am_offset) >> rshift; // Top Cymbal: this uses the envelope from operator 17 (channel 8), // and the operator 13/17 phase select to compute the phase uint32_t phase = 0x100 | (phase_select << 9); result += opl_emu_fm_operator_compute_volume(fmch->m_op[1], phase, am_offset) >> rshift; result = opl_emu_clamp(result, -clipmax - 1, clipmax); // add to the output opl_emu_fm_channel_add_to_output(fmch, fmch->m_choffs, output, result * 2); } // This is the number subtracted from the 2nd voice for an instrument for OP2 soundbanks // which causes those second voices to be replaced before their (more important) first voices // when the OPL voice channels are all used up #define OP2_2NDVOICE_PRIORITY_PENALTY 0xFF struct voicealloc_t { unsigned short priority; signed short timbreid; signed char channel; signed char note; unsigned char voiceindex; /* 1 if 2nd voice for OP2 soundbank instrument, 0 otherwise */ }; enum op2_flags_t { OP2_FIXEDPITCH = 1, OP2_UNUSED = 2, /* technically delayed vibrato https://moddingwiki.shikadi.net/wiki/OP2_Bank_Format */ OP2_DOUBLEVOICE = 4, }; struct opl_t { signed char notes2voices[16][128][2]; /* keeps the map of channel:notes -> voice allocations */ unsigned short channelpitch[16]; /* per-channel pitch level */ unsigned short channelvol[16]; /* per-channel pitch level */ struct voicealloc_t voices2notes[18]; /* keeps the map of what voice is playing what note/channel currently */ unsigned char channelprog[16]; /* programs (patches) assigned to channels */ int opl3; /* flag indicating whether or not the sound module is OPL3-compatible or only OPL2 */ struct opl_emu_t opl_emu; struct opl_timbre_t opl_gmtimbres[ 256 ]; struct opl_timbre_t opl_gmtimbres_voice2[ 256 ]; /* second voice included in OP2 format */ int is_op2; /* true if OP2 soundbank */ enum op2_flags_t op2_flags[ 256 ]; /* OP2 format flags */ }; void oplregwr( opl_t* opl, uint16_t reg, uint8_t data ) { opl_emu_write( &opl->opl_emu, reg, data ); } void opl_render( opl_t* opl, short* sample_pairs, int sample_pairs_count, float volume ) { memset( sample_pairs, 0, sample_pairs_count * 2 * sizeof( short ) ); opl_emu_generate( &opl->opl_emu, sample_pairs, sample_pairs_count, volume ); } void opl_write( opl_t* opl, int count, unsigned short* regs, unsigned char* data ) { struct opl_emu_t* emu = &opl->opl_emu; for( int i = 0; i < count; ++i ) { uint16_t regnum = regs[ i ]; uint8_t value = data[ i ]; // 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); continue;; } // 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, value, &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); } } } } const unsigned short freqtable[128] = { /* note # */ 345, 365, 387, 410, 435, 460, 488, 517, 547, 580, 615, 651, /* 0 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 12 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 24 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 36 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 48 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 60 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 72 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 84 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 96 */ 690, 731, 774, 820, 869, 921, 975, 517, 547, 580, 615, 651, /* 108 */ 690, 731, 774, 820, 869, 921, 975, 517}; /* 120 */ const unsigned char octavetable[128] = { /* note # */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, /* 12 */ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, /* 24 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, /* 36 */ 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, /* 48 */ 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, /* 60 */ 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, /* 72 */ 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, /* 84 */ 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, /* 96 */ 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, /* 108 */ 9, 9, 9, 9, 9, 9, 9,10}; /* 120 */ const unsigned short pitchtable[256] = { /* pitch wheel */ 29193U,29219U,29246U,29272U,29299U,29325U,29351U,29378U, /* -128 */ 29405U,29431U,29458U,29484U,29511U,29538U,29564U,29591U, /* -120 */ 29618U,29644U,29671U,29698U,29725U,29752U,29778U,29805U, /* -112 */ 29832U,29859U,29886U,29913U,29940U,29967U,29994U,30021U, /* -104 */ 30048U,30076U,30103U,30130U,30157U,30184U,30212U,30239U, /* -96 */ 30266U,30293U,30321U,30348U,30376U,30403U,30430U,30458U, /* -88 */ 30485U,30513U,30541U,30568U,30596U,30623U,30651U,30679U, /* -80 */ 30706U,30734U,30762U,30790U,30817U,30845U,30873U,30901U, /* -72 */ 30929U,30957U,30985U,31013U,31041U,31069U,31097U,31125U, /* -64 */ 31153U,31181U,31209U,31237U,31266U,31294U,31322U,31350U, /* -56 */ 31379U,31407U,31435U,31464U,31492U,31521U,31549U,31578U, /* -48 */ 31606U,31635U,31663U,31692U,31720U,31749U,31778U,31806U, /* -40 */ 31835U,31864U,31893U,31921U,31950U,31979U,32008U,32037U, /* -32 */ 32066U,32095U,32124U,32153U,32182U,32211U,32240U,32269U, /* -24 */ 32298U,32327U,32357U,32386U,32415U,32444U,32474U,32503U, /* -16 */ 32532U,32562U,32591U,32620U,32650U,32679U,32709U,32738U, /* -8 */ 32768U,32798U,32827U,32857U,32887U,32916U,32946U,32976U, /* 0 */ 33005U,33035U,33065U,33095U,33125U,33155U,33185U,33215U, /* 8 */ 33245U,33275U,33305U,33335U,33365U,33395U,33425U,33455U, /* 16 */ 33486U,33516U,33546U,33576U,33607U,33637U,33667U,33698U, /* 24 */ 33728U,33759U,33789U,33820U,33850U,33881U,33911U,33942U, /* 32 */ 33973U,34003U,34034U,34065U,34095U,34126U,34157U,34188U, /* 40 */ 34219U,34250U,34281U,34312U,34343U,34374U,34405U,34436U, /* 48 */ 34467U,34498U,34529U,34560U,34591U,34623U,34654U,34685U, /* 56 */ 34716U,34748U,34779U,34811U,34842U,34874U,34905U,34937U, /* 64 */ 34968U,35000U,35031U,35063U,35095U,35126U,35158U,35190U, /* 72 */ 35221U,35253U,35285U,35317U,35349U,35381U,35413U,35445U, /* 80 */ 35477U,35509U,35541U,35573U,35605U,35637U,35669U,35702U, /* 88 */ 35734U,35766U,35798U,35831U,35863U,35895U,35928U,35960U, /* 96 */ 35993U,36025U,36058U,36090U,36123U,36155U,36188U,36221U, /* 104 */ 36254U,36286U,36319U,36352U,36385U,36417U,36450U,36483U, /* 112 */ 36516U,36549U,36582U,36615U,36648U,36681U,36715U,36748U}; /* 120 */ /* * This file contains OPL fonts for all the General Midi level 1 instruments: * - 128 instruments * - 128 percussions */ static struct opl_timbre_t opl_gmtimbres_default[ 256 ] = { /* ,---------+-------- Wave select settings * * | ,-------÷-+------ Sustain/release rates * * | | ,-----÷-÷-+---- Attack/decay rates * * | | | ,---÷-÷-÷-+-- AM/VIB/EG/KSR/Multiple bits * * | | | | | | | | * * | | | | | | | | ,----+-- KSL/attenuation settings * * | | | | | | | | | | ,----- Feedback/connection bits * * | | | | | | | | | | | ,---- Percussion note number */ {0x223E133,0x4F4F131,0xA5,0x09,0x0E,0,0}, {0x433F133,0x0F4F131,0xA3,0x09,0x0C,0,0}, {0x4B3E131,0x0F4F130,0x1A,0x86,0x08,0,0}, {0x073FA32,0x4F4D111,0x80,0x08,0x0C,0,0}, {0x0E7F21C,0x0B8F201,0x6F,0x80,0x0C,0,0}, {0x0C7F437,0x0D7F230,0x5D,0x0A,0x08,0,0}, {0x303F232,0x1F6D131,0x44,0x00,0x08,0,0}, {0x559F101,0x0F7F111,0x44,0x08,0x06,0,0}, {0x087F607,0x0E4F231,0x54,0x08,0x09,0,0}, {0x0A5F33F,0x0F2C312,0xA1,0x06,0x0C,0,0}, {0x004561A,0x004F601,0x9E,0x08,0x08,0,0}, {0x0F6F2B2,0x0F6F281,0xE5,0x00,0x0F,0,0}, {0x0F6F618,0x0F7E500,0x63,0x80,0x06,0,0}, {0x096F616,0x0F5F111,0x1F,0x03,0x04,0,0}, {0x082F307,0x0E3F302,0x97,0x8A,0x06,0,0}, {0x4109130,0x3B5F321,0x52,0x88,0x08,0,0}, {0x116B1A2,0x117D160,0x88,0x80,0x07,0,0}, {0x0F8F032,0x0F8F001,0x65,0x00,0x0E,0,0}, {0x018AA70,0x0088AB1,0x44,0x0A,0x04,0,0}, {0x1043030,0x1145431,0x92,0x80,0x0D,0,0}, {0x0178000,0x1176081,0x54,0x8B,0x04,0,0}, {0x025A721,0x1264132,0x4D,0x08,0x06,0,0}, {0x4FAF022,0x01A6221,0x96,0x08,0x0C,0,0}, {0x107F020,0x2055231,0x92,0x07,0x08,0,0}, {0x274A613,0x4B8F401,0xDD,0x05,0x06,0,0}, {0x5E5F133,0x1E4F211,0x99,0x07,0x06,0,0}, {0x21FF021,0x088F211,0xA5,0x48,0x0A,0,0}, {0x132ED10,0x3E7D210,0x87,0x0A,0x06,0,0}, {0x0F4E430,0x0F5F330,0x92,0x80,0x08,0,0}, {0x0F78111,0x3F7F054,0x40,0x45,0x08,0,0}, {0x0F78111,0x2F7F054,0x40,0x45,0x0A,0,0}, {0x6F78AE4,0x649B1F2,0x03,0x0A,0x0A,0,0}, {0x0209220,0x0E6C130,0x97,0x05,0x00,0,0}, {0x0FFF030,0x0F8F131,0x9D,0x00,0x0A,0,0}, {0x026AA20,0x0D7F131,0xCF,0x80,0x0A,0,0}, {0x0007511,0x0B69212,0x1A,0x40,0x08,0,0}, {0x2E69419,0x5B6B311,0x5E,0x08,0x00,0,0}, {0x2E69515,0x1B6B211,0x17,0x08,0x00,0,0}, {0x0F5F430,0x0F6F330,0x0E,0x00,0x0A,0,0}, {0x1468330,0x017D231,0x15,0x00,0x0A,0,0}, {0x2257020,0x4266161,0x95,0x05,0x0A,0,0}, {0x2446070,0x2154130,0x4E,0x00,0x0A,0,0}, {0x214D070,0x1175222,0x0F,0x88,0x02,0,0}, {0x521F570,0x4166021,0x90,0x09,0x06,0,0}, {0x201C3B0,0x0058321,0x8D,0x08,0x08,0,0}, {0x0848521,0x074C411,0x69,0x07,0x0A,0,0}, {0x022E133,0x0F2F131,0xA2,0x09,0x0E,0,0}, {0x4C3C413,0x0B4D215,0x9B,0x09,0x0A,0,0}, {0x223F832,0x4055421,0x99,0x8A,0x0C,0,0}, {0x5059022,0x1055521,0x5B,0x85,0x00,0,0}, {0x1254732,0x40256B1,0xA4,0x0A,0x08,0,0}, {0x0031121,0x1046120,0x58,0x84,0x00,0,0}, {0x0055020,0x0F55021,0x1C,0x0F,0x07,0,0}, {0x239B420,0x0076121,0x50,0x05,0x06,0,0}, {0x05470F0,0x07460B0,0x5A,0x80,0x00,0,0}, {0x2436110,0x114D211,0x90,0x00,0x0C,0,0}, {0x017B561,0x0097F21,0x92,0x04,0x0C,0,0}, {0x0235270,0x0198160,0x1E,0x08,0x0E,0,0}, {0x0157621,0x0378261,0x94,0x00,0x0C,0,0}, {0x118537A,0x5177432,0x21,0x00,0x04,0,0}, {0x0364120,0x02B7220,0x21,0x08,0x0C,0,0}, {0x0577320,0x117C020,0x19,0x03,0x0C,0,0}, {0x036F120,0x337F120,0x92,0x08,0x0E,0,0}, {0x0A66120,0x0976120,0x9B,0x08,0x0E,0,0}, {0x0F37010,0x1F65051,0x51,0x04,0x0A,0,0}, {0x1067021,0x1165231,0x8A,0x00,0x06,0,0}, {0x00B9820,0x10B5330,0x8E,0x00,0x0A,0,0}, {0x10B8020,0x11B6330,0x87,0x00,0x08,0,0}, {0x0235030,0x0076C62,0x58,0x08,0x0A,0,0}, {0x2077820,0x2074331,0x97,0x00,0x06,0,0}, {0x0199030,0x01B6132,0x95,0x80,0x0A,0,0}, {0x0177531,0x0174530,0x93,0x03,0x0C,0,0}, {0x08D6EF1,0x02A3571,0xC0,0x00,0x0E,0,0}, {0x08860A1,0x02A6561,0xA6,0x00,0x08,0,0}, {0x2176522,0x0277421,0x5A,0x00,0x06,0,0}, {0x1273471,0x01745B0,0x8D,0x05,0x04,0,0}, {0x00457E1,0x0375760,0xA8,0x00,0x0E,0,0}, {0x6543739,0x25D67A1,0x28,0x00,0x0E,0,0}, {0x00F31D0,0x0053270,0xC7,0x00,0x0B,0,0}, {0x00581A1,0x0295230,0x37,0x00,0x06,0,0}, {0x20FFF22,0x60FFF21,0x7F,0x12,0x05,0,0}, {0x39BC120,0x368C030,0xBF,0x06,0x00,0,0}, {0x33357F0,0x00767E0,0x28,0x00,0x0E,0,0}, {0x40457E1,0x03D67E0,0x23,0x00,0x0E,0,0}, {0x32B7320,0x12BF131,0x40,0x00,0x08,0,0}, {0x5029071,0x0069060,0x96,0x09,0x08,0,0}, {0x195C120,0x1637030,0x43,0x80,0x0A,0,0}, {0x132ED10,0x3E7D210,0x87,0x08,0x06,0,0}, {0x2946374,0x005A0A1,0xA5,0x05,0x02,0,0}, {0x00521A1,0x0053360,0xC0,0x00,0x09,0,0}, {0x2A5A120,0x196A120,0x95,0x05,0x0C,0,0}, {0x005F0E0,0x0548160,0x44,0x00,0x0B,0,0}, {0x0336183,0x05452E0,0xA7,0x00,0x06,0,0}, {0x2529082,0x1534340,0x9D,0x80,0x0C,0,0}, {0x2345231,0x2135120,0x98,0x00,0x06,0,0}, {0x1521161,0x1632060,0x90,0x80,0x08,0,0}, {0x157B260,0x019F803,0x04,0x40,0x07,0,0}, {0x2322122,0x0133221,0x8C,0x92,0x06,0,0}, {0x074F624,0x0249303,0xC0,0x0D,0x00,0,0}, {0x3D2C091,0x1D2D130,0x8E,0x03,0x00,0,0}, {0x5F29052,0x0F2C240,0x96,0x06,0x08,0,0}, {0x05213E1,0x2131371,0x1A,0x88,0x07,0,0}, {0x0B67060,0x0928031,0x9C,0x11,0x0A,0,0}, {0x0025511,0x1748201,0x94,0x06,0x0E,0,0}, {0x0B37120,0x5F48220,0x1B,0x08,0x02,0,0}, {0x0127530,0x6F4F310,0x0D,0x0A,0x06,0,0}, {0x033F900,0x273F400,0x80,0x80,0x00,0,0}, {0x332F320,0x6E49423,0x0E,0x08,0x08,0,0}, {0x0328413,0x073B410,0xA1,0x00,0x0F,0,0}, {0x1397931,0x2099B22,0x80,0x00,0x06,0,0}, {0x302A130,0x0266221,0x1E,0x00,0x0E,0,0}, {0x0136030,0x1169130,0x12,0x80,0x08,0,0}, {0x032A115,0x172B212,0x00,0x80,0x01,0,0}, {0x001E795,0x0679616,0x81,0x00,0x04,0,0}, {0x4046303,0x005A901,0xCA,0x08,0x06,0,0}, {0x6D1F817,0x098F611,0xA7,0x00,0x06,0,0}, {0x008F312,0x004F600,0x08,0xC8,0x04,0,46}, {0x0C8A820,0x0B7E601,0x0B,0x00,0x00,0,0}, {0x00437D2,0x0343471,0xA1,0x07,0x0C,0,0}, {0x2114109,0x51D2101,0x05,0x80,0x0A,0,0}, {0x4543310,0x3574515,0x19,0x03,0x0E,0,0}, {0x00437D2,0x0343471,0xA1,0x07,0x0C,0,0}, {0x200C327,0x6021300,0x80,0x08,0x0E,0,65}, {0x003EBD7,0x06845D8,0xD4,0x00,0x07,0,0}, {0x62FDA20,0x614B009,0x42,0x48,0x04,0,0}, {0x101FE30,0x6142120,0x00,0x00,0x0C,0,17}, {0x200832F,0x6044020,0x80,0x00,0x0E,0,65}, {0x230732F,0x6E6F400,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x06,0,0}, {0x0000000,0x0000000,0x00,0x00,0x06,0,0}, {0x0000000,0x0000000,0x00,0x00,0x06,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0A,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0A,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x06,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0A,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0C,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0A,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x02,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0C,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0E,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x08,0,0}, {0x0000000,0x0000000,0x00,0x00,0x0F,0,0}, {0x057FB00,0x046F800,0x00,0x00,0x0E,0,25}, {0x277F810,0x006FC11,0x86,0x00,0x0A,0,34}, {0x287F702,0x678F802,0x80,0x88,0x0A,0,61}, {0x6EF8801,0x608B502,0x0D,0x00,0x0C,0,38}, {0x05476C1,0x30892C5,0x80,0x08,0x06,0,37}, {0x6EF8800,0x608F502,0x13,0x00,0x08,0,38}, {0x508F601,0x104F600,0x08,0x00,0x06,0,32}, {0x254F307,0x307F905,0x04,0x0B,0x06,0,48}, {0x508F601,0x104F600,0x08,0x00,0x0A,0,34}, {0x254D307,0x3288905,0x04,0x08,0x0A,0,48}, {0x508F601,0x104F600,0x0C,0x00,0x08,0,37}, {0x2F2E327,0x3F5C525,0x04,0x08,0x08,0,48}, {0x508F601,0x104F600,0x0C,0x00,0x00,0,40}, {0x508F601,0x104F600,0x0C,0x00,0x00,0,43}, {0x292F108,0x354F201,0x00,0x08,0x00,0,61}, {0x508F601,0x104F600,0x0C,0x00,0x00,0,46}, {0x210F509,0x305FE03,0x8A,0x88,0x00,0,60}, {0x283E108,0x334D700,0x00,0x08,0x00,0,79}, {0x2E1F119,0x3F3F11B,0x04,0x08,0x00,0,62}, {0x2777603,0x3679601,0x87,0x08,0x00,0,80}, {0x251F206,0x263C504,0x04,0x09,0x00,0,67}, {0x366F905,0x099F701,0x00,0x00,0x00,0,58}, {0x292F108,0x354F201,0x00,0x03,0x00,0,62}, {0x422F120,0x056B40E,0x81,0x00,0x00,0,24}, {0x212FD04,0x305FD03,0x01,0x00,0x00,0,61}, {0x2A8F9E3,0x0779643,0x1E,0x08,0x00,0,41}, {0x2A8F9E3,0x0779643,0x1E,0x00,0x00,0,35}, {0x0A8F7E9,0x5D8990A,0x08,0x00,0x00,0,29}, {0x2A8F9E2,0x0779642,0x1E,0x00,0x00,0,41}, {0x2A8F9E2,0x0779642,0x1E,0x00,0x00,0,37}, {0x456FB02,0x017F700,0x81,0x00,0x00,0,55}, {0x456FB02,0x017F700,0x81,0x00,0x00,0,48}, {0x367FD01,0x098F601,0x00,0x08,0x00,0,78}, {0x367FD01,0x098F601,0x00,0x08,0x00,0,73}, {0x25E780C,0x32B8A0A,0x00,0x80,0x00,0,88}, {0x098600F,0x3FC8590,0x08,0xC0,0x00,0,40}, {0x009F020,0x37DA588,0x07,0x00,0x00,0,45}, {0x00FC020,0x32DA5A8,0x07,0x00,0x00,0,42}, {0x106F680,0x016F610,0x00,0x00,0x00,0,48}, {0x106F680,0x016F610,0x00,0x00,0x00,0,48}, {0x0D1F815,0x078F512,0x44,0x00,0x00,0,73}, {0x1D1F813,0x078F512,0x44,0x00,0x00,0,68}, {0x1D1F813,0x078F512,0x44,0x00,0x00,0,61}, {0x1DC5D01,0x06FF79F,0x0B,0x00,0x00,0,16}, {0x1C7C900,0x05FF49F,0x07,0x00,0x00,0,16}, {0x160F2C6,0x07AF4D4,0x4F,0x80,0x00,0,90}, {0x160F286,0x0B7F294,0x4F,0x80,0x00,0,90}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0}, {0x0000000,0x0000000,0x00,0x00,0x00,0,0} }; /* tables below provide register offsets for each voice. note, that these are * NOT the registers IDs, but their direct offsets instead - this for simpler * and faster computations. */ const unsigned short op1offsets[18] = {0x00,0x01,0x02,0x08,0x09,0x0a,0x10,0x11,0x12,0x100,0x101,0x102,0x108,0x109,0x10a,0x110,0x111,0x112}; const unsigned short op2offsets[18] = {0x03,0x04,0x05,0x0b,0x0c,0x0d,0x13,0x14,0x15,0x103,0x104,0x105,0x10b,0x10c,0x10d,0x113,0x114,0x115}; /* number of melodic voices: 9 by default (OPL2), can go up to 18 (OPL3) */ static int voicescount = 9; /* 'volume' is in range 0..127 - take care to change only the 'attenuation' * part of the register, and never touch the KSL bits */ static void calc_vol(unsigned char *regbyte, int volume) { int level; /* invert bits and strip out the KSL header */ level = ~(*regbyte); level &= 0x3f; /* adjust volume */ level = (level * volume) / 127; /* boundaries check */ if (level > 0x3f) level = 0x3f; if (level < 0) level = 0; /* invert the bits, as expected by the OPL registers */ level = ~level; level &= 0x3f; /* final result computation */ *regbyte &= 0xC0; /* zero out all attentuation bits */ *regbyte |= level; /* fill in the new attentuation value */ } /* 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) { /* init memory */ struct opl_t* opl = (struct opl_t*)calloc(1, sizeof(struct opl_t)); if (opl == NULL) return NULL; memcpy( opl->opl_gmtimbres, opl_gmtimbres_default, sizeof( opl_gmtimbres_default ) ); opl_emu_init( &opl->opl_emu ); opl->is_op2 = 0; /* detect the hardware and return error if not found */ oplregwr(opl, 0x04, 0x60); /* reset both timers by writing 60h to register 4 */ oplregwr(opl, 0x04, 0x80); /* enable interrupts by writing 80h to register 4 (must be a separate write from the 1st one) */ //x = inp(port) & 0xE0; /* read the status register (port 388h) and store the result */ oplregwr(opl, 0x02, 0xff); /* write FFh to register 2 (Timer 1) */ oplregwr(opl, 0x04, 0x21); /* start timer 1 by writing 21h to register 4 */ /*udelay(500);*/ /* Creative Labs recommends a delay of at least 80 microseconds I delay for 500us just to be sure. DO NOT perform inp() calls for delay here, some cards do not initialize well then (reported for CT2760) */ //y = inp(port) & 0xE0; /* read the upper bits of the status register */ oplregwr(opl, 0x04, 0x60); /* reset both timers and interrupts (see steps 1 and 2) */ oplregwr(opl, 0x04, 0x80); /* reset both timers and interrupts (see steps 1 and 2) */ /* test the stored results of steps 3 and 7 by ANDing them with E0h. The result of step 3 should be */ // if (x != 0) return(-1); /* 00h, and the result of step 7 should be C0h. If both are */ // if (y != 0xC0) return(-2); /* ok, an AdLib-compatible board is installed in the computer */ /* is it an OPL3 or just an OPL2? */ /*if ((inp(port) & 0x06) == 0) */opl->opl3 = 1; /* init the hardware */ voicescount = 9; /* OPL2 provides 9 melodic voices */ /* enable OPL3 (if detected) and put it into 36 operators mode */ if (opl->opl3 != 0) { oplregwr(opl, 0x105, 1); /* enable OPL3 mode (36 operators) */ oplregwr(opl, 0x104, 0); /* disable four-operator voices */ voicescount = 18; /* OPL3 provides 18 melodic channels */ /* Init the secondary OPL chip * NOTE: this I don't do anymore, it turns my Aztech Waverider mute! */ /* oplregwr(0x101, 0x20); */ /* enable Waveform Select */ /* oplregwr(0x108, 0x40); */ /* turn off CSW mode and activate FM synth mode */ /* oplregwr(0x1BD, 0x00); */ /* set vibrato/tremolo depth to low, set melodic mode */ } oplregwr(opl, 0x01, 0x20); /* enable Waveform Select */ oplregwr(opl, 0x04, 0x00); /* turn off timers IRQs */ oplregwr(opl, 0x08, 0x40); /* turn off CSW mode and activate FM synth mode */ oplregwr(opl, 0xBD, 0x00); /* set vibrato/tremolo depth to low, set melodic mode */ for (int x = 0; x < voicescount; x++) { oplregwr(opl, 0x20 + op1offsets[x], 0x1); /* set the modulator's multiple to 1 */ oplregwr(opl, 0x20 + op2offsets[x], 0x1); /* set the modulator's multiple to 1 */ oplregwr(opl, 0x40 + op1offsets[x], 0x10); /* set volume of all channels to about 40 dB */ oplregwr(opl, 0x40 + op2offsets[x], 0x10); /* set volume of all channels to about 40 dB */ } opl_clear(opl); /* all done */ return(opl); } /* close OPL device */ void opl_destroy(opl_t* opl) { int x; /* turns all notes 'off' */ opl_clear(opl); /* set volume to lowest level on all voices */ for (x = 0; x < voicescount; x++) { oplregwr(opl, 0x40 + op1offsets[x], 0x1f); oplregwr(opl, 0x40 + op2offsets[x], 0x1f); } /* if OPL3, switch the chip back into its default OPL2 mode */ if (opl->opl3 != 0) oplregwr(opl, 0x105, 0); /* free state memory */ free(opl); opl = NULL; } void opl_noteoff(opl_t* opl, unsigned short voice) { /* if voice is one of the OPL3 set, adjust it and route over secondary OPL port */ if (voice >= 9) { oplregwr(opl, 0x1B0 + voice - 9, 0); } else { oplregwr(opl, 0xB0 + voice, 0); } } void opl_noteon(opl_t* opl, unsigned short voice, unsigned int note, int pitch) { unsigned int freq = freqtable[note]; unsigned int octave = octavetable[note]; if (pitch != 0) { if (pitch > 127) { pitch = 127; } else if (pitch < -128) { pitch = -128; } freq = ((unsigned long)freq * pitchtable[pitch + 128]) >> 15; if (freq >= 1024) { freq >>= 1; octave++; } } if (octave > 7) octave = 7; /* if voice is one of the OPL3 set, adjust it and route over secondary OPL port */ if (voice >= 9) { voice -= 9; voice |= 0x100; } oplregwr(opl, 0xA0 + voice, freq & 0xff); /* set lowfreq */ oplregwr(opl, 0xB0 + voice, (freq >> 8) | (octave << 2) | 32); /* KEY ON + hifreq + octave */ } /* turns off all notes */ void opl_clear(opl_t* opl) { int x, y; for (x = 0; x < voicescount; x++) opl_noteoff(opl, x); /* reset the percussion bits at the 0xBD register */ oplregwr(opl, 0xBD, 0); /* mark all voices as unused */ for (x = 0; x < voicescount; x++) { opl->voices2notes[x].channel = -1; opl->voices2notes[x].note = -1; opl->voices2notes[x].timbreid = -1; opl->voices2notes[x].voiceindex = 0xFF; } /* mark all notes as unallocated */ for (x = 0; x < 16; x++) { for (y = 0; y < 128; y++) { opl->notes2voices[x][y][0] = -1; opl->notes2voices[x][y][1] = -1; } } /* pre-set emulated channel patches to default GM ids and reset all * per-channel volumes */ for (x = 0; x < 16; x++) { opl_midi_changeprog(opl,x, x); opl->channelvol[x] = 127; } } void opl_midi_pitchwheel(opl_t* opl, int channel, int pitchwheel) { int x; /* update the new pitch value for channel (used by newly played notes) */ // opl->channelpitch[channel] = pitchwheel; /* check all active voices to see who is playing on given channel now, and * recompute all playing notes for this channel with the new pitch TODO */ for (x = 0; x < voicescount; x++) { if (opl->voices2notes[x].channel != channel) continue; opl_timbre_t* timbre = opl->voices2notes[x].voiceindex == 0 ? &(opl->opl_gmtimbres[opl->voices2notes[x].timbreid]) : &(opl->opl_gmtimbres_voice2[opl->voices2notes[x].timbreid]) ; opl_noteon(opl, x, opl->voices2notes[x].note + timbre->noteoffset, pitchwheel + timbre->finetune); } } void opl_midi_controller(opl_t* opl, int channel, int id, int value) { int x; switch (id) { case 11: /* "Expression" (meaning "channel volume") */ opl->channelvol[channel] = value; break; case 123: /* 'all notes off' */ case 120: /* 'all sound off' - I map it to 'all notes off' for now, not perfect but better than not handling it at all */ for (x = 0; x < voicescount; x++) { if (opl->voices2notes[x].channel != channel) continue; opl_midi_noteoff(opl, channel, opl->voices2notes[x].note); } break; } } /* assign a new instrument to emulated MIDI channel */ void opl_midi_changeprog(opl_t* opl,int channel, int program) { if (channel == 9) return; /* do not allow to change channel 9, it is for percussions only */ opl->channelprog[channel] = program; } void opl_loadinstrument(opl_t* opl, int voice, opl_timbre_t *timbre) { /* KSL (key level scaling) / attenuation */ oplregwr(opl, 0x40 + op1offsets[voice], timbre->modulator_40); oplregwr(opl, 0x40 + op2offsets[voice], timbre->carrier_40 | 0x3f); /* force volume to 0, it will be reajusted during 'note on' */ /* select waveform on both operators */ oplregwr(opl, 0xE0 + op1offsets[voice], timbre->modulator_E862 >> 24); oplregwr(opl, 0xE0 + op2offsets[voice], timbre->carrier_E862 >> 24); /* sustain / release */ oplregwr(opl, 0x80 + op1offsets[voice], (timbre->modulator_E862 >> 16) & 0xff); oplregwr(opl, 0x80 + op2offsets[voice], (timbre->carrier_E862 >> 16) & 0xff); /* attack rate / decay */ oplregwr(opl, 0x60 + op1offsets[voice], (timbre->modulator_E862 >> 8) & 0xff); oplregwr(opl, 0x60 + op2offsets[voice], (timbre->carrier_E862 >> 8) & 0xff); /* AM / vibrato / envelope */ oplregwr(opl, 0x20 + op1offsets[voice], timbre->modulator_E862 & 0xff); oplregwr(opl, 0x20 + op2offsets[voice], timbre->carrier_E862 & 0xff); /* feedback / connection */ if (voice >= 9) { voice -= 9; voice |= 0x100; } if (opl->opl3 != 0) { /* on OPL3 make sure to enable LEFT/RIGHT unmute bits */ oplregwr(opl, 0xC0 + voice, timbre->feedconn | 0x30); } else { oplregwr(opl, 0xC0 + voice, timbre->feedconn); } } /* adjust the volume of the voice (in the usual MIDI range of 0..127) */ static void voicevolume(opl_t* opl, unsigned short voice, const opl_timbre_t* timbre, int volume) { unsigned char carrierval = timbre->carrier_40; if (volume == 0) { carrierval |= 0x3f; } else { calc_vol(&carrierval, volume); } oplregwr(opl, 0x40 + op2offsets[voice], carrierval); } /* get the id of the instrument that relates to channel/note pair */ static int getinstrument(opl_t* opl, int channel, int note) { if ((note < 0) || (note > 127) || (channel > 15)) return(-1); if (channel == 9) { /* the percussion channel requires special handling */ if (opl->is_op2) return 128 + note - 35; else return(128 | note); } return(opl->channelprog[channel]); } void opl_midi_noteoff_op2(opl_t* opl, int channel, int note, int vindex); void opl_midi_noteon_op2(opl_t* opl, int channel, int note, int velocity, int vindex) { if( velocity == 0 ) { opl_midi_noteoff_op2( opl, channel, note, vindex ); return; } int x, voice = -1; int lowestpriority = 0xFFFF; int highestvoiceindex = -1; int lowestpriorityvoice = -1; int instrument; /* get the instrument to play */ instrument = getinstrument(opl, channel, note); if (instrument < 0) return; /* only play OP2 second voice when appropriate */ if (vindex > 0 && (opl->op2_flags[instrument] & OP2_DOUBLEVOICE) == 0) return; opl_timbre_t* timbre = vindex == 0 ? &(opl->opl_gmtimbres[instrument]) : &(opl->opl_gmtimbres_voice2[instrument]); /* if note already playing, then reuse its voice to avoid leaving a stuck voice */ if (opl->notes2voices[channel][note][vindex] >= 0) { voice = opl->notes2voices[channel][note][vindex]; } else { /* else find a free voice, possibly with the right timbre, or at least locate the oldest note */ for (x = 0; x < voicescount; x++) { if (opl->voices2notes[x].channel < 0) { voice = x; /* preselect this voice, but continue looking */ /* if the instrument is right, do not look further */ if (opl->voices2notes[x].timbreid == instrument && opl->voices2notes[x].voiceindex == vindex) { break; } } if (opl->voices2notes[x].priority < lowestpriority) { /* 2nd instrumental voice should not overwrite 1st instrumental voice */ /* also prefer 2nd instrumental voices when possible */ if (opl->voices2notes[x].voiceindex >= vindex && opl->voices2notes[x].voiceindex >= highestvoiceindex) { lowestpriorityvoice = x; lowestpriority = opl->voices2notes[x].priority; highestvoiceindex = opl->voices2notes[x].voiceindex; } } } /* if no free voice available, then abort the oldest one */ if (voice < 0) { if (lowestpriorityvoice < 0) { /* no suitable voice found to abort */ return; } voice = lowestpriorityvoice; opl_midi_noteoff_op2(opl, opl->voices2notes[voice].channel, opl->voices2notes[voice].note, opl->voices2notes[voice].voiceindex); } } /* load the proper instrument, if not already good */ if (opl->voices2notes[voice].timbreid != instrument) { opl->voices2notes[voice].timbreid = instrument; opl_loadinstrument(opl, voice, timbre); } /* update states */ opl->voices2notes[voice].channel = channel; opl->voices2notes[voice].note = note; opl->voices2notes[voice].priority = ((16 - channel) << 8) | 0xff; /* lower channels must have priority */ opl->voices2notes[voice].voiceindex = vindex; opl->notes2voices[channel][note][vindex] = voice; /* second OP2 voice has lower priority */ if (vindex != 0) { int reducedprio = (int)opl->voices2notes[voice].priority - OP2_2NDVOICE_PRIORITY_PENALTY; if (reducedprio < 0) reducedprio = 0; opl->voices2notes[voice].priority = (unsigned short)reducedprio; } /* set the requested velocity on the voice */ voicevolume(opl, voice, timbre, velocity * opl->channelvol[channel] / 127); /* trigger NOTE_ON on the OPL, take care to apply the 'finetune' pitch correction, too */ if (channel == 9) { /* percussion channel doesn't provide a real note, so I */ /* use a static one (MUSPLAYER uses C-5 (60), why not. */ opl_noteon(opl, voice, timbre->notenum + timbre->noteoffset, opl->channelpitch[channel] + timbre->finetune); } else { opl_noteon(opl, voice, note + timbre->noteoffset, opl->channelpitch[channel] + timbre->finetune); } /* reajust all priorities */ for (x = 0; x < voicescount; x++) { if (opl->voices2notes[x].priority > 0) opl->voices2notes[x].priority -= 1; } } void opl_midi_noteon(opl_t* opl, int channel, int note, int velocity) { /* play 2nd instrumental voice first just in case */ opl_midi_noteon_op2(opl, channel, note, velocity, 1); opl_midi_noteon_op2(opl, channel, note, velocity, 0); } void opl_midi_noteoff_op2(opl_t* opl, int channel, int note, int vindex) { int voice = opl->notes2voices[channel][note][vindex]; if (voice >= 0) { opl_noteoff(opl, voice); opl->voices2notes[voice].channel = -1; opl->voices2notes[voice].note = -1; opl->voices2notes[voice].priority = -1; opl->voices2notes[voice].voiceindex = 0xFF; opl->notes2voices[channel][note][vindex] = -1; } } void opl_midi_noteoff(opl_t* opl, int channel, int note) { opl_midi_noteoff_op2(opl, channel, note, 0); opl_midi_noteoff_op2(opl, channel, note, 1); } static int opl_loadbank_internal(opl_t* opl, char const* file, int offset) { opl->is_op2 = 0; unsigned char buff[16]; int i; /* open the IBK file */ FILE* f = fopen( file, "rb" ); if( !f ) return -1; /* file must be exactly 3204 bytes long */ fseek( f, 0, SEEK_END ); if (ftell(f) != 3204) { fclose(f); return(-2); } fseek( f, 0, SEEK_SET); /* file must start with an IBK header */ if ((fread(buff, 1, 4,f) != 4) || (buff[0] != 'I') || (buff[1] != 'B') || (buff[2] != 'K') || (buff[3] != 0x1A)) { fclose(f); return(-3); } /* load 128 instruments from the IBK file */ for (i = offset; i < 128 + offset; i++) { /* load instruments */ if (fread(buff, 1, 16, f) != 16) { fclose(f); return(-4); } /* load modulator */ opl->opl_gmtimbres[i].modulator_E862 = buff[8]; /* wave select */ opl->opl_gmtimbres[i].modulator_E862 <<= 8; opl->opl_gmtimbres[i].modulator_E862 |= buff[6]; /* sust/release */ opl->opl_gmtimbres[i].modulator_E862 <<= 8; opl->opl_gmtimbres[i].modulator_E862 |= buff[4]; /* attack/decay */ opl->opl_gmtimbres[i].modulator_E862 <<= 8; opl->opl_gmtimbres[i].modulator_E862 |= buff[0]; /* AM/VIB... flags */ /* load carrier */ opl->opl_gmtimbres[i].carrier_E862 = buff[9]; /* wave select */ opl->opl_gmtimbres[i].carrier_E862 <<= 8; opl->opl_gmtimbres[i].carrier_E862 |= buff[7]; /* sust/release */ opl->opl_gmtimbres[i].carrier_E862 <<= 8; opl->opl_gmtimbres[i].carrier_E862 |= buff[5]; /* attack/decay */ opl->opl_gmtimbres[i].carrier_E862 <<= 8; opl->opl_gmtimbres[i].carrier_E862 |= buff[1]; /* AM/VIB... flags */ /* load KSL */ opl->opl_gmtimbres[i].modulator_40 = buff[2]; opl->opl_gmtimbres[i].carrier_40 = buff[3]; /* feedconn & finetune */ opl->opl_gmtimbres[i].feedconn = buff[10]; opl->opl_gmtimbres[i].finetune = buff[12]; /* used only in some IBK files */ opl->opl_gmtimbres[i].notenum = 60; opl->opl_gmtimbres[i].noteoffset = 0; } /* close file and return success */ fclose(f); return(0); } /* static void dump2file(void) { FILE *fd; int i; fd = fopen("dump.txt", "wb"); if (fd == NULL) return; for (i = 0; i < 256; i++) { char *comma = ""; if (i < 255) comma = ","; fprintf(fd, "{0x%07lX,0x%07lX,0x%02X,0x%02X,0x%02X,%d,%d}%s\r\n", opl->opl_gmtimbres[i].modulator_E862, opl->opl_gmtimbres[i].carrier_E862, opl->opl_gmtimbres[i].modulator_40, opl->opl_gmtimbres[i].carrier_40, opl->opl_gmtimbres[i].feedconn, opl->opl_gmtimbres[i].finetune, opl->opl_gmtimbres[i].notenum, comma); } fclose(fd); } */ int opl_loadbank_ibk(opl_t* opl, char const* file) { char *instruments = NULL, *percussion = NULL; int i, res; instruments = strdup(file); /* duplicate the string so we can modify it */ if (instruments == NULL) return(-64); /* out of mem */ /* if a second file is provided, it's for percussion */ for (i = 0; instruments[i] != 0; i++) { if (instruments[i] == ',') { instruments[i] = 0; percussion = instruments + i + 1; break; } } /* load the file(s) */ res = opl_loadbank_internal(opl, instruments, 0); if ((res == 0) && (percussion != NULL)) { res = opl_loadbank_internal(opl, percussion, 128); } free(instruments); /*dump2file();*/ /* dump instruments to a 'dump.txt' file */ return(res); } static void opl_load_op2_voice(opl_timbre_t* timbre, uint8_t const* buff) { /* load modulator */ timbre->modulator_E862 = buff[3]; /* wave select */ timbre->modulator_E862 <<= 8; timbre->modulator_E862 |= buff[2]; /* sust/release */ timbre->modulator_E862 <<= 8; timbre->modulator_E862 |= buff[1]; /* attack/decay */ timbre->modulator_E862 <<= 8; timbre->modulator_E862 |= buff[0]; /* AM/VIB... flags */ /* load carrier */ timbre->carrier_E862 = buff[10]; /* wave select */ timbre->carrier_E862 <<= 8; timbre->carrier_E862 |= buff[9]; /* sust/release */ timbre->carrier_E862 <<= 8; timbre->carrier_E862 |= buff[8]; /* attack/decay */ timbre->carrier_E862 <<= 8; timbre->carrier_E862 |= buff[7]; /* AM/VIB... flags */ /* load KSL */ timbre->modulator_40 = ( buff[5] & 0x3f ) | ( buff[4] & 0xc0 ); timbre->carrier_40 = ( buff[12] & 0x3f ) | ( buff[11] & 0xc0 ); /* feedconn & finetune */ timbre->feedconn = buff[6]; timbre->finetune = 0; timbre->noteoffset = (int16_t)(buff[14] | ((uint16_t)buff[15] << 8)); } int opl_loadbank_op2(opl_t* opl, void const* data, int size ) { if( size < 8 + 36 * 175 ) { return -3; } uint8_t const* buff = (uint8_t const*) data; int i; /* file must start with an #OPL_II# header */ if ((buff[0] != '#') || (buff[1] != 'O') || (buff[2] != 'P') || (buff[3] != 'L') || (buff[4] != '_') || (buff[5] != 'I') || (buff[6] != 'I') || (buff[7] != '#')) { return(-3); } buff += 8; opl->is_op2 = 1; /* load 128 instruments from the IBK file */ for (i = 0; i < 175; i++) { /* load instruments */ /* OP2 instrument header */ opl->op2_flags[i] = (enum op2_flags_t)( buff[0] | ((uint16_t)buff[1] << 8) ); int finetune = buff[2]; uint8_t fixednote = buff[3]; buff += 4; /* first voice */ opl_load_op2_voice(&opl->opl_gmtimbres[i], buff); opl->opl_gmtimbres[i].notenum = fixednote; buff += 16; /* second voice */ opl_load_op2_voice(&opl->opl_gmtimbres_voice2[i], buff); opl->opl_gmtimbres_voice2[i].notenum = fixednote; opl->opl_gmtimbres_voice2[i].finetune += finetune - 128; buff += 16; } /* close file and return success */ return(0); } #endif /* OPL_IMPLEMENTATION */ /* OPL_EMU parts are from https://github.com/aaronsgiles/ymfm License: BSD 3-Clause License Copyright (c) 2021, Aaron Giles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Other parts are from http://dosmid.sourceforge.net License: Library to access OPL2/OPL3 hardware (YM3812 / YMF262) Copyright (C) 2015-2016 Mateusz Viste All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */