From 386a7ca442933f957ed206df33a87dd31233b766 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Mon, 4 May 2020 21:14:51 -0400 Subject: [PATCH] Continues doing away with the attempt heavily to interleave the OPLL and OPL2, creating a new OPLL class. --- .../OPL2/Implementation/EnvelopeGenerator.hpp | 1 + Components/OPL2/Implementation/OPLBase.hpp | 40 ++++ .../OPL2/Implementation/WaveformGenerator.hpp | 8 +- Components/OPL2/OPL2.cpp | 87 +++---- Components/OPL2/OPL2.hpp | 21 +- Components/OPL2/OPLL.cpp | 215 ++++++++++++++++++ Components/OPL2/OPLL.hpp | 102 +++++++++ Machines/MasterSystem/MasterSystem.cpp | 2 +- .../Clock Signal.xcodeproj/project.pbxproj | 14 +- 9 files changed, 420 insertions(+), 70 deletions(-) create mode 100644 Components/OPL2/Implementation/OPLBase.hpp create mode 100644 Components/OPL2/OPLL.cpp create mode 100644 Components/OPL2/OPLL.hpp diff --git a/Components/OPL2/Implementation/EnvelopeGenerator.hpp b/Components/OPL2/Implementation/EnvelopeGenerator.hpp index d4bb37426..af721781b 100644 --- a/Components/OPL2/Implementation/EnvelopeGenerator.hpp +++ b/Components/OPL2/Implementation/EnvelopeGenerator.hpp @@ -11,6 +11,7 @@ #include <optional> #include <functional> +#include "LowFrequencyOscillator.hpp" namespace Yamaha { namespace OPL { diff --git a/Components/OPL2/Implementation/OPLBase.hpp b/Components/OPL2/Implementation/OPLBase.hpp new file mode 100644 index 000000000..6b6a71038 --- /dev/null +++ b/Components/OPL2/Implementation/OPLBase.hpp @@ -0,0 +1,40 @@ +// +// OPLBase.hpp +// Clock Signal +// +// Created by Thomas Harte on 03/05/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef OPLBase_h +#define OPLBase_h + +#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp" +#include "../../../Concurrency/AsyncTaskQueue.hpp" + +namespace Yamaha { +namespace OPL { + +template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource { + public: + void write(uint16_t address, uint8_t value) { + if(address & 1) { + static_cast<Child *>(this)->write_register(selected_register_, value); + } else { + selected_register_ = value; + } + } + + protected: + OPLBase(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {} + + Concurrency::DeferringAsyncTaskQueue &task_queue_; + + private: + uint8_t selected_register_ = 0; +}; + +} +} + +#endif /* OPLBase_h */ diff --git a/Components/OPL2/Implementation/WaveformGenerator.hpp b/Components/OPL2/Implementation/WaveformGenerator.hpp index 0008dd3cf..381fe7eea 100644 --- a/Components/OPL2/Implementation/WaveformGenerator.hpp +++ b/Components/OPL2/Implementation/WaveformGenerator.hpp @@ -15,12 +15,12 @@ namespace Yamaha { namespace OPL { +enum class Waveform { + Sine, HalfSine, AbsSine, PulseSine +}; + template <int phase_precision> class WaveformGenerator { public: - enum class Waveform { - Sine, HalfSine, AbsSine, PulseSine - }; - /*! @returns The output of waveform @c form at [integral] phase @c phase. */ diff --git a/Components/OPL2/OPL2.cpp b/Components/OPL2/OPL2.cpp index b12af5b3b..6fc19ca59 100644 --- a/Components/OPL2/OPL2.cpp +++ b/Components/OPL2/OPL2.cpp @@ -18,6 +18,8 @@ using namespace Yamaha::OPL; +/* + template <typename Child> OPLBase<Child>::OPLBase(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {} @@ -79,43 +81,42 @@ void OPLL::get_samples(std::size_t number_of_samples, std::int16_t *target) { audio_offset_ = (audio_offset_ + 1) % update_period; } - -/* // Fill in any leftover from the previous session. - if(audio_offset_) { - while(audio_offset_ < update_period && number_of_samples) { - *target = int16_t(channels_[audio_offset_ / channel_output_period].level); - ++target; - ++audio_offset_; - --number_of_samples; - } - audio_offset_ = 0; - } - - // End now if that provided everything that was asked for. - if(!number_of_samples) return; - - int total_updates = int(number_of_samples) / update_period; - number_of_samples %= size_t(update_period); - audio_offset_ = int(number_of_samples); - - while(total_updates--) { - update_all_chanels(); - - for(int c = 0; c < update_period; ++c) { - *target = int16_t(channels_[c / channel_output_period].level); - ++target; - } - } - - // If there are any other spots remaining, fill them. - if(number_of_samples) { - update_all_chanels(); - - for(int c = 0; c < int(number_of_samples); ++c) { - *target = int16_t(channels_[c / channel_output_period].level); - ++target; - } - }*/ +// // Fill in any leftover from the previous session. +// if(audio_offset_) { +// while(audio_offset_ < update_period && number_of_samples) { +// *target = int16_t(channels_[audio_offset_ / channel_output_period].level); +// ++target; +// ++audio_offset_; +// --number_of_samples; +// } +// audio_offset_ = 0; +// } +// +// // End now if that provided everything that was asked for. +// if(!number_of_samples) return; +// +// int total_updates = int(number_of_samples) / update_period; +// number_of_samples %= size_t(update_period); +// audio_offset_ = int(number_of_samples); +// +// while(total_updates--) { +// update_all_chanels(); +// +// for(int c = 0; c < update_period; ++c) { +// *target = int16_t(channels_[c / channel_output_period].level); +// ++target; +// } +// } +// +// // If there are any other spots remaining, fill them. +// if(number_of_samples) { +// update_all_chanels(); +// +// for(int c = 0; c < int(number_of_samples); ++c) { +// *target = int16_t(channels_[c / channel_output_period].level); +// ++target; +// } +// } } void OPLL::set_sample_volume_range(std::int16_t range) { @@ -274,9 +275,9 @@ void OPLL::update_all_chanels() { #undef VOLUME } -/* -template <Personality personality> -void OPL2<personality>::get_samples(std::size_t number_of_samples, std::int16_t *target) { + +//template <Personality personality> +//void OPL2<personality>::get_samples(std::size_t number_of_samples, std::int16_t *target) { // TODO. // out = exp(logsin(phase2 + exp(logsin(phase1) + gain1)) + gain2) @@ -301,9 +302,9 @@ void OPL2<personality>::get_samples(std::size_t number_of_samples, std::int16_t // Tom tom, using operator 14, // Cymbal, using operator 17; and // Symbol, using operator 13. -} +//} + -*/ void OPL2::write_register(uint8_t address, uint8_t value) { @@ -393,3 +394,5 @@ uint8_t OPL2::read(uint16_t address) { // b5 = timer 2 flag return 0xff; } + +*/ diff --git a/Components/OPL2/OPL2.hpp b/Components/OPL2/OPL2.hpp index e9949c73a..a583c0848 100644 --- a/Components/OPL2/OPL2.hpp +++ b/Components/OPL2/OPL2.hpp @@ -20,24 +20,7 @@ namespace Yamaha { namespace OPL { -template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource { - public: - void write(uint16_t address, uint8_t value); - - protected: - OPLBase(Concurrency::DeferringAsyncTaskQueue &task_queue); - - Concurrency::DeferringAsyncTaskQueue &task_queue_; - LowFrequencyOscillator oscillator_; - - uint8_t depth_rhythm_control_; - uint8_t csm_keyboard_split_; - bool waveform_enable_; - - private: - uint8_t selected_register_ = 0; -}; - +/* struct OPL2: public OPLBase<OPL2> { public: // Creates a new OPL2. @@ -132,7 +115,7 @@ struct OPLL: public OPLBase<OPLL> { int audio_offset_ = 0; std::atomic<int> total_volume_; -}; +};*/ } } diff --git a/Components/OPL2/OPLL.cpp b/Components/OPL2/OPLL.cpp new file mode 100644 index 000000000..121bee38d --- /dev/null +++ b/Components/OPL2/OPLL.cpp @@ -0,0 +1,215 @@ +// +// OPLL.cpp +// Clock Signal +// +// Created by Thomas Harte on 03/05/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#include "OPLL.hpp" + +#include <cassert> + +using namespace Yamaha::OPL; + +OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider, bool is_vrc7): + OPLBase(task_queue), audio_divider_(audio_divider), is_vrc7_(is_vrc7) { + // Due to the way that sound mixing works on the OPLL, the audio divider may not + // be larger than 4. + assert(audio_divider <= 4); + + // Set up proper damping management. + for(int c = 0; c < 9; ++c) { + envelope_generators_[c].set_should_damp([this, c] { + // Propagate attack mode to the modulator, and reset both phases. + envelope_generators_[c + 9].set_key_on(true); + phase_generators_[c + 0].reset(); + phase_generators_[c + 9].reset(); + }); + } +} + +// MARK: - Machine-facing programmatic input. + +void OPLL::write_register(uint8_t address, uint8_t value) { + // The OPLL doesn't have timers or other non-audio functions, so all writes + // go to the audio queue. + task_queue_.defer([this, address, value] { + // The first 8 locations are used to define the custom instrument, and have + // exactly the same format as the patch set arrays at the head of this file. + if(address < 8) { + custom_instrument_[address] = value; + + // Update all channels that refer to instrument 0. + for(int c = 0; c < 9; ++c) { + if(!channels_[c].instrument) { + install_instrument(c); + } + } + + return; + } + + // Register 0xe enables or disables rhythm mode and contains the + // percussion key-on bits. + if(address == 0xe) { + rhythm_mode_enabled_ = value & 0x20; + rhythm_generators_[0].set_key_on(value & 0x01); + rhythm_generators_[1].set_key_on(value & 0x02); + rhythm_generators_[2].set_key_on(value & 0x04); + rhythm_generators_[3].set_key_on(value & 0x08); + rhythm_generators_[4].set_key_on(value & 0x10); + return; + } + + // That leaves only per-channel selections, for which the addressing + // is completely orthogonal; check that a valid channel is being requested. + const auto index = address & 0xf; + if(index > 8) return; + + switch(address & 0xf0) { + default: break; + + // Address 1x sets the low 8 bits of the period for channel x. + case 0x10: + channels_[index].period = (channels_[index].period & ~0xff) | value; + set_channel_period(index); + return; + + // Address 2x Sets the octave and a single bit of the frequency, as well + // as setting key on and sustain mode. + case 0x20: + channels_[index].period = (channels_[index].period & 0xff) | (value & 1); + channels_[index].octave = (value >> 1) & 7; + set_channel_period(index); + + // In this implementation the first 9 envelope generators are for + // channel carriers, and their will_attack callback is used to trigger + // key-on for modulators. But key-off needs to be set to both envelope + // generators now. + if(value & 0x10) { + envelope_generators_[index].set_key_on(true); + } else { + envelope_generators_[index + 0].set_key_on(false); + envelope_generators_[index + 9].set_key_on(false); + } + + // Set sustain bit to both the relevant operators. + channels_[index].use_sustain = value & 0x20; + set_use_sustain(index); + return; + + // Address 3x selects the instrument and attenuation for a channel; + // in rhythm mode some of the nibbles that ordinarily identify instruments + // instead nominate additional attenuations. This code reads those back + // from the stored instrument values. + case 0x30: + channels_[index].instrument = value >> 4; + channels_[index].attenuation = value >> 4; + install_instrument(index); + return; + } + }); +} + +void OPLL::set_channel_period(int channel) { + phase_generators_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave); + phase_generators_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave); + + key_level_scalers_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave); + key_level_scalers_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave); +} + +const uint8_t *OPLL::instrument_definition(int instrument) { + // Instrument 0 is the custom instrument. + if(!instrument) return custom_instrument_; + + // Instruments other than 0 are taken from the fixed set. + const int index = (instrument - 1) * 8; + return is_vrc7_ ? &vrc7_patch_set[index] : &opll_patch_set[index]; +} + +void OPLL::install_instrument(int channel) { + auto &carrier_envelope = envelope_generators_[channel + 0]; + auto &carrier_phase = phase_generators_[channel + 0]; + auto &carrier_scaler = key_level_scalers_[channel + 0]; + + auto &modulator_envelope = envelope_generators_[channel + 9]; + auto &modulator_phase = phase_generators_[channel + 9]; + auto &modulator_scaler = key_level_scalers_[channel + 9]; + + const uint8_t *const instrument = instrument_definition(channels_[channel].instrument); + + // Bytes 0 (modulator) and 1 (carrier): + // + // b0-b3: multiplier; + // b4: key-scale rate enable; + // b5: sustain-level enable; + // b6: vibrato enable; + // b7: tremolo enable. + modulator_phase.set_multiple(instrument[0] & 0xf); + channels_[channel].modulator_key_rate_scale_multiplier = (instrument[0] >> 4) & 1; + modulator_phase.set_vibrato_enabled(instrument[0] & 0x40); + modulator_envelope.set_tremolo_enabled(instrument[0] & 0x80); + + carrier_phase.set_multiple(instrument[1] & 0xf); + channels_[channel].carrier_key_rate_scale_multiplier = (instrument[1] >> 4) & 1; + carrier_phase.set_vibrato_enabled(instrument[1] & 0x40); + carrier_envelope.set_tremolo_enabled(instrument[1] & 0x80); + + // Pass off bit 5. + set_use_sustain(channel); + + // Byte 2: + // + // b0–b5: modulator attenuation; + // b6–b7: modulator key-scale level. + modulator_scaler.set_key_scaling_level(instrument[3] >> 6); + channels_[channel].modulator_attenuation = instrument[2] & 0x3f; + + // Byte 3: + // + // b0–b2: modulator feedback level; + // b3: modulator waveform selection; + // b4: carrier waveform selection; + // b5: [unused] + // b6–b7: carrier key-scale level. + channels_[channel].modulator_feedback = instrument[3] & 7; + channels_[channel].modulator_waveform = Waveform((instrument[3] >> 3) & 1); + channels_[channel].carrier_waveform = Waveform((instrument[3] >> 4) & 1); + carrier_scaler.set_key_scaling_level(instrument[3] >> 6); + + // Bytes 4 (modulator) and 5 (carrier): + // + // b0–b3: decay rate; + // b4–b7: attack rate. + modulator_envelope.set_decay_rate(instrument[4] & 0xf); + modulator_envelope.set_attack_rate(instrument[4] >> 4); + carrier_envelope.set_decay_rate(instrument[5] & 0xf); + carrier_envelope.set_attack_rate(instrument[5] >> 4); + + // Bytes 6 (modulator) and 7 (carrier): + // + // b0–b3: release rate; + // b4–b7: sustain level. + modulator_envelope.set_release_rate(instrument[6] & 0xf); + modulator_envelope.set_sustain_level(instrument[6] >> 4); + carrier_envelope.set_release_rate(instrument[7] & 0xf); + carrier_envelope.set_release_rate(instrument[7] >> 4); +} + +void OPLL::set_use_sustain(int channel) { + const uint8_t *const instrument = instrument_definition(channels_[channel].instrument); + envelope_generators_[channel + 0].set_sustain_level((instrument[1] & 0x20) || channels_[channel].use_sustain); + envelope_generators_[channel + 9].set_sustain_level((instrument[0] & 0x20) || channels_[channel].use_sustain); +} + +// MARK: - Output generation. + +void OPLL::set_sample_volume_range(std::int16_t range) { + total_volume_ = range; +} + +void OPLL::get_samples(std::size_t number_of_samples, std::int16_t *target) { + // TODO. +} diff --git a/Components/OPL2/OPLL.hpp b/Components/OPL2/OPLL.hpp new file mode 100644 index 000000000..192977204 --- /dev/null +++ b/Components/OPL2/OPLL.hpp @@ -0,0 +1,102 @@ +// +// OPLL.hpp +// Clock Signal +// +// Created by Thomas Harte on 03/05/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef OPLL_hpp +#define OPLL_hpp + +#include "Implementation/OPLBase.hpp" +#include "Implementation/EnvelopeGenerator.hpp" +#include "Implementation/KeyLevelScaler.hpp" +#include "Implementation/PhaseGenerator.hpp" +#include "Implementation/LowFrequencyOscillator.hpp" +#include "Implementation/WaveformGenerator.hpp" + +#include <atomic> + +namespace Yamaha { +namespace OPL { + +class OPLL: public OPLBase<OPLL> { + public: + /// Creates a new OPLL or VRC7. + OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider = 1, bool is_vrc7 = false); + + /// As per ::SampleSource; provides audio output. + void get_samples(std::size_t number_of_samples, std::int16_t *target); + void set_sample_volume_range(std::int16_t range); + + /// Reads from the OPL. + uint8_t read(uint16_t address); + + private: + friend OPLBase<OPLL>; + void write_register(uint8_t address, uint8_t value); + + int audio_divider_ = 0; + std::atomic<int> total_volume_; + + static constexpr int period_precision = 9; + static constexpr int envelope_precision = 9; + + // Standard melodic phase and envelope generators. + PhaseGenerator<period_precision> phase_generators_[18]; + EnvelopeGenerator<envelope_precision, period_precision> envelope_generators_[18]; + KeyLevelScaler<period_precision> key_level_scalers_[18]; + + // Dedicated rhythm envelope generators and attenuations. + EnvelopeGenerator<envelope_precision, period_precision> rhythm_generators_[5]; + int rhythm_attenuations_[5]; + + // Channel specifications. + struct Channel { + int octave = 0; + int period = 0; + int instrument = 0; + + int attenuation = 0; + int modulator_attenuation = 0; + + Waveform carrier_waveform = Waveform::Sine; + Waveform modulator_waveform = Waveform::Sine; + + int carrier_key_rate_scale_multiplier = 0; + int modulator_key_rate_scale_multiplier = 0; + + int modulator_feedback = 0; + + bool use_sustain = false; + } channels_[9]; + + // The low-frequency oscillator. + LowFrequencyOscillator oscillator_; + bool rhythm_mode_enabled_ = false; + bool is_vrc7_ = false; + + // Contains the current configuration of the custom instrument. + uint8_t custom_instrument_[8]; + + // Helpers to push per-channel information. + + /// Pushes the current octave and period to channel @c channel. + void set_channel_period(int channel); + + /// Installs the appropriate instrument on channel @c channel. + void install_instrument(int channel); + + /// Sets whether the sustain level is used for channel @c channel based on its current instrument + /// and the user's selection. + void set_use_sustain(int channel); + + /// @returns The 8-byte definition of instrument @c instrument. + const uint8_t *instrument_definition(int instrument); +}; + +} +} + +#endif /* OPLL_hpp */ diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index c6cfcc518..c153be338 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -12,7 +12,7 @@ #include "../../Components/9918/9918.hpp" #include "../../Components/SN76489/SN76489.hpp" -#include "../../Components/OPL2/OPL2.hpp" +#include "../../Components/OPL2/OPLL.hpp" #include "../MachineTypes.hpp" #include "../../Configurable/Configurable.hpp" diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index a63d45e50..21e8789d3 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -222,6 +222,8 @@ 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; }; 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */; }; + 4B619099245FBF7B0013F202 /* OPLL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B619097245FBF7B0013F202 /* OPLL.cpp */; }; + 4B61909A245FBF7B0013F202 /* OPLL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B619097245FBF7B0013F202 /* OPLL.cpp */; }; 4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */; }; 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; }; 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; }; @@ -793,8 +795,6 @@ 4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC131782346DF2B00E4FF3D /* MSA.cpp */; }; 4BC57CD92436A62900FBC404 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CD82436A62900FBC404 /* State.cpp */; }; 4BC57CDA2436A62900FBC404 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CD82436A62900FBC404 /* State.cpp */; }; - 4BC57CE22436BFE000FBC404 /* OPL2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CE02436BFE000FBC404 /* OPL2.cpp */; }; - 4BC57CE32436BFE000FBC404 /* OPL2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC57CE02436BFE000FBC404 /* OPL2.cpp */; }; 4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */; }; 4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; @@ -1152,6 +1152,9 @@ 4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EnvelopeGenerator.hpp; sourceTree = "<group>"; }; 4B619094245E73B90013F202 /* KeyLevelScaler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyLevelScaler.hpp; sourceTree = "<group>"; }; 4B619095245FA04B0013F202 /* WaveformGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = WaveformGenerator.hpp; sourceTree = "<group>"; }; + 4B619096245FBEF80013F202 /* OPLBase.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = OPLBase.hpp; path = Implementation/OPLBase.hpp; sourceTree = "<group>"; }; + 4B619097245FBF7B0013F202 /* OPLL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OPLL.cpp; sourceTree = "<group>"; }; + 4B619098245FBF7B0013F202 /* OPLL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = OPLL.hpp; sourceTree = "<group>"; }; 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = "<group>"; }; 4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = "<group>"; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; }; @@ -3575,6 +3578,9 @@ 4BC0CB2F2447EC7C00A79DBB /* Implementation */, 4BC57CE02436BFE000FBC404 /* OPL2.cpp */, 4BC57CE12436BFE000FBC404 /* OPL2.hpp */, + 4B619096245FBEF80013F202 /* OPLBase.hpp */, + 4B619097245FBF7B0013F202 /* OPLL.cpp */, + 4B619098245FBF7B0013F202 /* OPLL.hpp */, ); path = OPL2; sourceTree = "<group>"; @@ -4375,7 +4381,6 @@ 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */, 4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */, - 4BC57CE32436BFE000FBC404 /* OPL2.cpp in Sources */, 4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */, 4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */, 4BD5D2692199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */, @@ -4530,6 +4535,7 @@ 4B894537201967B4007DE474 /* Z80.cpp in Sources */, 4B055A9F1FAE85DA0060FFFF /* HFE.cpp in Sources */, 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */, + 4B61909A245FBF7B0013F202 /* OPLL.cpp in Sources */, 4B055AEC1FAE9BA20060FFFF /* Z80Base.cpp in Sources */, 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */, @@ -4651,7 +4657,6 @@ 4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */, 4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */, 4BAE495920328897004BE78E /* ZX8081OptionsPanel.swift in Sources */, - 4BC57CE22436BFE000FBC404 /* OPL2.cpp in Sources */, 4B89451A201967B4007DE474 /* ConfidenceSummary.cpp in Sources */, 4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */, 4B54C0C51F8D91D90050900F /* Keyboard.cpp in Sources */, @@ -4702,6 +4707,7 @@ 4BD0FBC3233706A200148981 /* CSApplication.m in Sources */, 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */, 4B89449520194CB3007DE474 /* MachineForTarget.cpp in Sources */, + 4B619099245FBF7B0013F202 /* OPLL.cpp in Sources */, 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */, 4B7BA03423C58B1F00B98D9E /* STX.cpp in Sources */, 4B6A4C991F58F09E00E3F787 /* 6502Base.cpp in Sources */,