diff --git a/src/hardware/dbopl.cpp b/src/hardware/dbopl.cpp index 99e45c90..6845b076 100644 --- a/src/hardware/dbopl.cpp +++ b/src/hardware/dbopl.cpp @@ -27,8 +27,7 @@ //TODO Don't delay first operator 1 sample in opl3 mode //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? - //TODO don't use variables in work structure for tremolo and vibrato but give the variables as parameters to GetSample - //TODO Since the vibrato takes 1024 samples it's easier to run the emulator in same vibrato chunks, vibrato would be costfree + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not //DUNNO Keyon in 4op, switch to 2op without keyoff. */ @@ -47,22 +46,31 @@ namespace DBOPL { -#define MAX_SAMPLES 256 #define OPLRATE ((double)(14318180.0 / 288.0)) - -//Only need 4 valid bits at the top for vibrato -#define VIBRATO_SH ( 32 - 4 ) -//Need 6 bits of accuracy -#define TREMOLO_SH ( 32 - 6 ) #define TREMOLO_TABLE 52 +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION //Wave bits available in the top of the 32bit range -//Original adlib uses 10.10, we use 12.20 -//Have to keep some bits in the top to allow for freqmul 0.5 -#define WAVE_BITS 12 +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif #define WAVE_SH ( 32 - WAVE_BITS ) #define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + //Maximum amount of attenuation bits //Envelope goes to 511, 9 bits #if (DBOPL_WAVE == WAVE_TABLEMUL ) @@ -271,21 +279,6 @@ static const WaveHandler WaveHandlerTable[8] = { #endif -//Structto hold the data everything well yeh works with -static struct { - Bitu samples; - Bits vibrato; - Bits tremolo; - inline void SetVibrato( Bit8s vib ) { - vibrato = vib; - vibrato &= ~0x80; - } - Bit32s output[MAX_SAMPLES * 2]; - //Could intermix the vib/trem table for slightly better cache hits - Bit8s vibTable[MAX_SAMPLES]; - Bit8s tremTable[MAX_SAMPLES]; -} Work; - /* Operator */ @@ -343,11 +336,20 @@ inline void Operator::UpdateAttenuation( ) { void Operator::UpdateFrequency( ) { Bit32u freq = chanData & (( 1 << 10 ) - 1); Bit32u block = (chanData >> 10) & 0xff; - - waveAdd = (freq << block) * freqMul; +#ifdef WAVE_PRECISION + block = 7 - block; + waveAdd = ( freq * freqMul ) >> block; +#else + waveAdd = ( freq << block ) * freqMul; +#endif if ( reg20 & MASK_VIBRATO ) { vibStrength = (Bit8u)(freq >> 7); + +#ifdef WAVE_PRECISION + vibrato = ( vibStrength * freqMul ) >> block; +#else vibrato = ( vibStrength << block ) * freqMul; +#endif } else { vibStrength = 0; vibrato = 0; @@ -378,7 +380,7 @@ INLINE Bit32s Operator::RateForward( Bit32u add ) { template< Operator::State yes> Bits Operator::TemplateVolume( ) { - Bit32s vol = activeLevel; + Bit32s vol = volume; Bit32s change; switch ( yes ) { case OFF: @@ -389,7 +391,7 @@ Bits Operator::TemplateVolume( ) { return vol; vol += ( (~vol) * change ) >> 3; if ( vol < ENV_MIN ) { - activeLevel = ENV_MIN; + volume = ENV_MIN; rateIndex = 0; SetState( DECAY ); return ENV_MIN; @@ -400,7 +402,7 @@ Bits Operator::TemplateVolume( ) { if ( vol >= sustainLevel ) { //Check if we didn't overshoot max attenuation, then just go off if ( vol >= ENV_MAX ) { - activeLevel = ENV_MAX; + volume = ENV_MAX; SetState( OFF ); return ENV_MAX; } @@ -417,13 +419,13 @@ Bits Operator::TemplateVolume( ) { case RELEASE: vol += RateForward( releaseAdd );; if ( vol >= ENV_MAX ) { - activeLevel = ENV_MAX; + volume = ENV_MAX; SetState( OFF ); return ENV_MAX; } break; } - activeLevel = vol; + volume = vol; return vol; } @@ -436,31 +438,15 @@ static const VolumeHandler VolumeHandlerTable[5] = { }; INLINE Bitu Operator::ForwardVolume() { - return totalLevel + (this->*volHandler)() -#if defined ( DBOPL_TREMOLO ) - + (Work.tremolo & tremoloMask) -#endif - ; + return currentLevel + (this->*volHandler)(); } INLINE Bitu Operator::ForwardWave() { -#if defined ( DBOPL_VIBRATO ) - if ( vibStrength >> (Bit8u)(Work.vibrato) ) { - Bit32s add = vibrato >> (Bit8u)(Work.vibrato); - //Sign extend over the shift value - Bit32s neg = Work.vibrato >> 16; - //Negate the add with -1 or 0 - add = ( add ^ neg ) - neg; - waveIndex += add + waveAdd; - return waveIndex >> WAVE_SH; - } -#endif - waveIndex += waveAdd; + waveIndex += waveCurrent; return waveIndex >> WAVE_SH; } - void Operator::Write20( const Chip* chip, Bit8u val ) { Bit8u change = (reg20 ^ val ); if ( !change ) @@ -539,12 +525,25 @@ INLINE void Operator::SetState( Bit8u s ) { } INLINE bool Operator::Silent() const { - if ( !ENV_SILENT( totalLevel + activeLevel ) ) + if ( !ENV_SILENT( totalLevel + volume ) ) return false; if ( !(rateZero & ( 1 << state ) ) ) return false; return true; -}; +} + +INLINE void Operator::Prepare( const Chip* chip ) { + currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); + waveCurrent = waveAdd; + if ( vibStrength >> chip->vibratoShift ) { + Bit32s add = vibrato >> chip->vibratoShift; + //Sign extend over the shift value + Bit32s neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + waveCurrent += add; + } +} void Operator::KeyOn( Bit8u mask ) { if ( !keyOn ) { @@ -590,7 +589,7 @@ Bits INLINE Operator::GetSample( Bits modulation ) { Bitu vol = ForwardVolume(); if ( ENV_SILENT( vol ) ) { //Simply forward the wave - waveIndex += waveAdd; + waveIndex += waveCurrent; return 0; } else { Bitu index = ForwardWave(); @@ -604,6 +603,7 @@ Operator::Operator() { freqMul = 0; waveIndex = 0; waveAdd = 0; + waveCurrent = 0; keyOn = 0; ksr = 0; reg20 = 0; @@ -614,8 +614,9 @@ Operator::Operator() { SetState( OFF ); rateZero = (1 << OFF); sustainLevel = ENV_MAX; - activeLevel = ENV_MAX; + currentLevel = ENV_MAX; totalLevel = ENV_MAX; + volume = ENV_MAX; } /* @@ -786,7 +787,7 @@ void Channel::ResetC0( const Chip* chip ) { }; template< bool opl3Mode> -void Channel::GeneratePercussion( Bit32s* output ) { +INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { Channel* chan = this; //BassDrum @@ -805,8 +806,8 @@ void Channel::GeneratePercussion( Bit32s* output ) { Operator* op2 = ( this + 1 )->Op(0); Operator* op4 = ( this + 2 )->Op(0); - //Precalculate stuff used by other oupts - Bit32u noiseBit = rand() & 0x2; + //Precalculate stuff used by other outputs + Bit32u noiseBit = (chip->ForwardNoise() & 0x1) << 1; Bit32u c2 = op2->ForwardWave(); //(bit 7 ^ bit 2) | bit 3 -> combined in bit 1 Bit32u phaseBit = ( (c2 >> 6) ^ ( c2 >> 1 ) ) | ( c2 >> 2 ); @@ -832,7 +833,10 @@ void Channel::GeneratePercussion( Bit32s* output ) { sample += op3->GetWave( sdIndex, sdVol ); } //Tom-tom - sample += op4->GetSample( 0 ); + Bit32u ttVol = op4->ForwardVolume(); + if ( !ENV_SILENT( ttVol ) ) { + sample += op4->GetWave( c4, ttVol ); + } //Top-Cymbal Operator* op5 = ( this + 2 )->Op(1); Bit32u tcVol = op5->ForwardVolume(); @@ -850,7 +854,7 @@ void Channel::GeneratePercussion( Bit32s* output ) { } template -Channel* Channel::BlockTemplate( ) { +Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { switch( mode ) { case sm2AM: case sm3AM: @@ -891,16 +895,24 @@ Channel* Channel::BlockTemplate( ) { } break; } - for ( Bitu i = 0; i < Work.samples; i++ ) { - Work.vibrato = Work.vibTable[i]; - Work.tremolo = Work.tremTable[i]; - + //Init the operators with the the current vibrato and tremolo values + Op( 0 )->Prepare( chip ); + Op( 1 )->Prepare( chip ); + if ( mode > sm4Start ) { + Op( 2 )->Prepare( chip ); + Op( 3 )->Prepare( chip ); + } + if ( mode > sm6Start ) { + Op( 4 )->Prepare( chip ); + Op( 5 )->Prepare( chip ); + } + for ( Bitu i = 0; i < samples; i++ ) { //Early out for percussion handlers if ( mode == sm2Percussion ) { - GeneratePercussion( Work.output + i ); + GeneratePercussion( chip, output + i ); continue; //Prevent some unitialized value bitching } else if ( mode == sm3Percussion ) { - GeneratePercussion( Work.output + i * 2 ); + GeneratePercussion( chip, output + i * 2 ); continue; //Prevent some unitialized value bitching } @@ -936,7 +948,7 @@ Channel* Channel::BlockTemplate( ) { switch( mode ) { case sm2AM: case sm2FM: - Work.output[ i ] += sample; + output[ i ] += sample; break; case sm3AM: case sm3FM: @@ -944,8 +956,8 @@ Channel* Channel::BlockTemplate( ) { case sm3AMFM: case sm3FMAM: case sm3AMAM: - Work.output[ i * 2 + 0 ] += sample & maskLeft; - Work.output[ i * 2 + 1 ] += sample & maskRight; + output[ i * 2 + 0 ] += sample & maskLeft; + output[ i * 2 + 1 ] += sample & maskRight; break; } } @@ -979,31 +991,53 @@ Chip::Chip() { opl3Active = 0; } - -Bit8u Chip::ForwardTremolo( ) { - tremoloCounter += tremoloAdd; - if ( tremoloCounter >= (TREMOLO_TABLE << TREMOLO_SH) ) { - tremoloCounter -= TREMOLO_TABLE << TREMOLO_SH; +INLINE Bit32u Chip::ForwardNoise() { + noiseCounter += noiseAdd; + Bitu count = noiseCounter >> LFO_SH; + noiseCounter &= WAVE_MASK; + for ( ; count > 0; --count ) { + //Noise calculation from mame + noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); + noiseValue >>= 1; } - Bitu index = tremoloCounter >> TREMOLO_SH; - return TremoloTable[ index ] >> tremoloShift; + return noiseValue; } -Bit8s Chip::ForwardVibrato( ) { - vibratoCounter += vibratoAdd; - Bitu index = vibratoCounter >> VIBRATO_SH; - //Vibrato shift, basically makes the shift greater reducing the actual final value - return VibratoTable[index & 7] + vibratoShift; +INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) { + //Current vibrato value, runs 4x slower than tremolo + vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; + vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; + tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; + + //Check hom many samples there can be done before the value changes + Bit32u todo = LFO_MAX - lfoCounter; + Bit32u count = (todo + lfoAdd - 1) / lfoAdd; + if ( count > samples ) { + count = samples; + lfoCounter += count * lfoAdd; + } else { + lfoCounter += count * lfoAdd; + lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + vibratoIndex = ( vibratoIndex + 1 ) & 31; + //Clip tremolo to the the table size + if ( tremoloIndex + 1 < TREMOLO_TABLE ) + ++tremoloIndex; + else + tremoloIndex = 0; + } + return count; } + void Chip::WriteBD( Bit8u val ) { Bit8u change = regBD ^ val; if ( !change ) return; regBD = val; //TODO could do this with shift and xor? - vibratoShift = (val & 0x40) ? 0x00 : 0x01; - tremoloShift = (val & 0x80) ? 0x00 : 0x02; + vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + tremoloStrength = (val & 0x80) ? 0x00 : 0x02; if ( val & 0x20 ) { //Drum was just enabled, make sure channel 6 has the right synth if ( change & 0x20 ) { @@ -1150,53 +1184,69 @@ Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { return 0; } -void Chip::GenerateBlock2( Bitu samples ) { - Work.samples = samples; - for ( Bitu i = 0; i < Work.samples; i++ ) { - Work.vibTable[i] = ForwardVibrato(); - Work.tremTable[i] = ForwardTremolo(); - Work.output[i] = 0; - } - int count = 0; - for( Channel* ch = chan; ch < chan + 9; ) { - count++; - ch = (ch->*(ch->synthHandler))(); +void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( total ); + for ( Bitu i = 0; i < samples; i++ ) { + output[i] = 0; + } + int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { + count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; } } -void Chip::GenerateBlock3( Bitu samples ) { - Work.samples = samples; - for ( Bitu i = 0; i < Work.samples; i++ ) { - Work.vibTable[i] = ForwardVibrato(); - Work.tremTable[i] = ForwardTremolo(); - Work.output[i*2 + 0] = 0; - Work.output[i*2 + 1] = 0; - } - int count = 0; - for( Channel* ch = chan; ch < chan + 18; ) { - count++; - ch = (ch->*(ch->synthHandler))(); +void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( total ); + for ( Bitu i = 0; i < samples; i++ ) { + output[i * 2 + 0 ] = 0; + output[i * 2 + 1 ] = 0; + } + int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { + count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; } } void Chip::Setup( Bit32u rate ) { - //Vibrato forwards every 1024 samples - vibratoAdd = (Bit32u)((double)rate * (double)( 1 << (VIBRATO_SH - 10) ) / OPLRATE); - vibratoCounter = 0; - //tremolo forwards every 64 samples - //We use a 52 entry table, real is 210, so repeat each sample an extra 4 times - tremoloAdd = (Bit32u)((double)rate * (double)( 1 << (TREMOLO_SH - 6 - 2) ) / OPLRATE); - tremoloCounter = 0; - //10 bits of frequency counter + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + //With higher octave this gets shifted up //-1 since the freqCreateTable = *2 - double scale = (OPLRATE * (double)( 1 << ( WAVE_SH - 10 - 1))) / rate; +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); for ( int i = 0; i < 16; i++ ) { - //Use rounding with 0.5 - freqMul[i] = (Bit32u)( 0.5 + scale * FreqCreateTable[ i ] ); + freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); } +#else + Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif - scale = OPLRATE / rate; //-3 since the real envelope takes 8 steps to reach the single value we supply for ( Bit8u i = 0; i < 76; i++ ) { Bit8u index, shift; @@ -1456,12 +1506,15 @@ void Handler::WriteReg( Bit32u addr, Bit8u val ) { } void Handler::Generate( MixerChannel* chan, Bitu samples ) { + Bit32s buffer[ 512 * 2 ]; + if ( samples > 512 ) + samples = 512; if ( !chip.opl3Active ) { - chip.GenerateBlock2( samples ); - chan->AddSamples_m32( samples, Work.output ); + chip.GenerateBlock2( samples, buffer ); + chan->AddSamples_m32( samples, buffer ); } else { - chip.GenerateBlock3( samples ); - chan->AddSamples_s32( samples, Work.output ); + chip.GenerateBlock3( samples, buffer ); + chan->AddSamples_s32( samples, buffer ); } } diff --git a/src/hardware/dbopl.h b/src/hardware/dbopl.h index 9232404f..c0e5367a 100644 --- a/src/hardware/dbopl.h +++ b/src/hardware/dbopl.h @@ -28,10 +28,6 @@ //Select the type of wave generator routine #define DBOPL_WAVE WAVE_TABLEMUL -//Enable vibrato in the output -#define DBOPL_VIBRATO -//Enable tremolo in the output -#define DBOPL_TREMOLO namespace DBOPL { @@ -44,20 +40,21 @@ typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); #endif typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); -typedef Channel* ( DBOPL::Channel::*SynthHandler) ( ); +typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); //Different synth modes that can generate blocks of data typedef enum { - smNone, sm2AM, sm2FM, - sm2Percussion, sm3AM, sm3FM, + sm4Start, sm3FMFM, sm3AMFM, sm3FMAM, sm3AMAM, + sm6Start, + sm2Percussion, sm3Percussion, } SynthMode; @@ -95,14 +92,16 @@ public: Bit32u waveStart; #endif Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index - Bit32u waveAdd; + Bit32u waveAdd; //The base frequency without vibrato + Bit32u waveCurrent; //waveAdd + vibratao Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? Bit32u vibrato; //Scaled up vibrato strength Bit32s sustainLevel; //When stopping at sustain level stop here - Bit32s totalLevel; //totalLeve is added to every generated volume - Bit32s activeLevel; //The currently active volume + Bit32s totalLevel; //totalLevel is added to every generated volume + Bit32u currentLevel; //totalLevel + tremolo + Bit32s volume; //The currently active volume Bit32u attackAdd; //Timers for the different states of the envelope Bit32u decayAdd; @@ -129,7 +128,7 @@ private: public: void UpdateAttenuation(); void UpdateRates( const Chip* chip ); - void UpdateFrequency( ); + void UpdateFrequency( ); void Write20( const Chip* chip, Bit8u val ); void Write40( const Chip* chip, Bit8u val ); @@ -138,6 +137,8 @@ public: void WriteE0( const Chip* chip, Bit8u val ); bool Silent() const; + void Prepare( const Chip* chip ); + void KeyOn( Bit8u mask); void KeyOff( Bit8u mask); @@ -182,20 +183,23 @@ struct Channel { //call this for the first channel template< bool opl3Mode > - void GeneratePercussion( Bit32s* output ); + void GeneratePercussion( Chip* chip, Bit32s* output ); //Generate blocks of data in specific modes template - Channel* BlockTemplate( ); + Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); Channel(); }; struct Chip { //This is used as the base counter for vibrato and tremolo - Bit32u tremoloCounter; - Bit32u tremoloAdd; - Bit32u vibratoCounter; - Bit32u vibratoAdd; + Bit32u lfoCounter; + Bit32u lfoAdd; + + + Bit32u noiseCounter; + Bit32u noiseAdd; + Bit32u noiseValue; //Frequency scales for the different multiplications Bit32u freqMul[16]; @@ -211,23 +215,29 @@ struct Chip { Bit8u reg08; Bit8u reg04; Bit8u regBD; + Bit8u vibratoIndex; + Bit8u tremoloIndex; + Bit8s vibratoSign; Bit8u vibratoShift; - Bit8u tremoloShift; + Bit8u tremoloValue; + Bit8u vibratoStrength; + Bit8u tremoloStrength; //Mask for allowed wave forms Bit8u waveFormMask; //0 or -1 when enabled Bit8s opl3Active; - Bit8u ForwardTremolo(); - Bit8s ForwardVibrato(); + //Return the maximum amount of samples before and LFO change + Bit32u ForwardLFO( Bit32u samples ); + Bit32u ForwardNoise(); void WriteBD( Bit8u val ); void WriteReg(Bit32u reg, Bit8u val ); Bit32u WriteAddr( Bit32u port, Bit8u val ); - void GenerateBlock2( Bitu samples ); - void GenerateBlock3( Bitu samples ); + void GenerateBlock2( Bitu samples, Bit32s* output ); + void GenerateBlock3( Bitu samples, Bit32s* output ); void Generate( Bit32u samples ); void Setup( Bit32u r );