Path: blob/a-new-beginning/Cherry/Core/audio/Sms_Apu.cpp
2 views
// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/12#include "Sms_Apu.h"34/* Copyright (C) 2003-2006 Shay Green. This module is free software; you5can redistribute it and/or modify it under the terms of the GNU Lesser6General Public License as published by the Free Software Foundation; either7version 2.1 of the License, or (at your option) any later version. This8module is distributed in the hope that it will be useful, but WITHOUT ANY9WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS10FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more11details. You should have received a copy of the GNU Lesser General Public12License along with this module; if not, write to the Free Software Foundation,13Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */1415#include "blargg_source.h"1617// Sms_Osc1819Sms_Osc::Sms_Osc()20{21output = 0;22outputs [0] = 0; // always stays NULL23outputs [1] = 0;24outputs [2] = 0;25outputs [3] = 0;26}2728void Sms_Osc::reset()29{30delay = 0;31last_amp = 0;32volume = 0;33output_select = 3;34output = outputs [3];35}3637// Sms_Square3839inline void Sms_Square::reset()40{41period = 0;42phase = 0;43Sms_Osc::reset();44}4546void Sms_Square::run( blip_time_t time, blip_time_t end_time )47{48int amp = volume;49if ( period > 128 )50amp = amp << 1 & -phase;5152{53int delta = amp - last_amp;54if ( delta )55{56last_amp = amp;57synth->offset( time, delta, output );58}59}6061time += delay;62delay = 0;63if ( period )64{65if ( time < end_time )66{67if ( !volume || period <= 128 ) // ignore 16kHz and higher68{69// keep calculating phase70int count = (end_time - time + period - 1) / period;71phase = (phase + count) & 1;72time += count * period;73}74else75{76Blip_Buffer* const output_ = this->output;77int delta = amp * 2 - volume * 2;78do79{80delta = -delta;81synth->offset_inline( time, delta, output_ );82time += period;83}84while ( time < end_time );8586last_amp = (delta >> 1) + volume;87phase = (delta >= 0);88}89}90delay = time - end_time;91}92}9394// Sms_Noise9596static int const noise_periods [3] = { 0x100, 0x200, 0x400 };9798inline void Sms_Noise::reset()99{100period = &noise_periods [0];101shifter = 0x8000;102feedback = 0x9000;103Sms_Osc::reset();104}105106void Sms_Noise::run( blip_time_t time, blip_time_t end_time )107{108int amp = (shifter & 1) ? 0 : volume;109110{111int delta = amp - last_amp;112if ( delta )113{114last_amp = amp;115synth.offset( time, delta, output );116}117}118119time += delay;120if ( !volume )121time = end_time;122123if ( time < end_time )124{125Blip_Buffer* const output_ = this->output;126unsigned shifter_ = this->shifter;127int delta = (shifter_ & 1) ? -volume : volume;128int period_ = *this->period * 2;129if ( !period_ )130period_ = 16;131132do133{134int changed = shifter_ + 1;135shifter_ = (feedback & -(shifter_ & 1)) ^ (shifter_ >> 1);136if ( changed & 2 ) // true if bits 0 and 1 differ137{138amp = (shifter_ & 1) ? 0 : volume;139delta = -delta;140synth.offset_inline( time, delta, output_ );141last_amp = amp;142}143time += period_;144}145while ( time < end_time );146147this->shifter = shifter_;148this->last_amp = (shifter_ & 1) ? 0 : volume; //delta >> 1;149}150delay = time - end_time;151}152153// Sms_Apu154155Sms_Apu::Sms_Apu()156{157for ( int i = 0; i < 3; i++ )158{159squares [i].synth = &square_synth;160oscs [i] = &squares [i];161}162oscs [3] = &noise;163164volume( 1.0 );165reset();166}167168Sms_Apu::~Sms_Apu()169{170}171172void Sms_Apu::volume( double vol )173{174vol *= 0.85 / (osc_count * 64 * 2);175square_synth.volume( vol );176noise.synth.volume( vol );177}178179void Sms_Apu::treble_eq( const blip_eq_t& eq )180{181square_synth.treble_eq( eq );182noise.synth.treble_eq( eq );183}184185void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )186{187require( (unsigned) index < osc_count );188require( (center && left && right) || (!center && !left && !right) );189Sms_Osc& osc = *oscs [index];190osc.outputs [1] = right;191osc.outputs [2] = left;192osc.outputs [3] = center;193osc.output = osc.outputs [osc.output_select];194}195196void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )197{198for ( int i = 0; i < osc_count; i++ )199osc_output( i, center, left, right );200}201202void Sms_Apu::reset( unsigned feedback, int noise_width )203{204last_time = 0;205latch = 0;206ggstereo_save = 0xFF;207208if ( !feedback || !noise_width )209{210feedback = 0x0003;211noise_width = 15;212}213// convert to "Galios configuration"214looped_feedback = 1 << (noise_width - 1);215noise_feedback = 0;216while ( noise_width-- )217{218noise_feedback = (noise_feedback << 1) | (feedback & 1);219feedback >>= 1;220}221222squares [0].reset();223squares [1].reset();224squares [2].reset();225noise.reset();226}227228void Sms_Apu::run_until( blip_time_t end_time )229{230require( end_time >= last_time ); // end_time must not be before previous time231232if ( end_time > last_time )233{234// run oscillators235for ( int i = 0; i < osc_count; ++i )236{237Sms_Osc& osc = *oscs [i];238if ( osc.output )239{240if ( i < 3 )241squares [i].run( last_time, end_time );242else243noise.run( last_time, end_time );244}245}246247last_time = end_time;248}249}250251void Sms_Apu::end_frame( blip_time_t end_time )252{253if ( end_time > last_time )254run_until( end_time );255256assert( last_time >= end_time );257last_time -= end_time;258}259260void Sms_Apu::write_ggstereo( blip_time_t time, int data )261{262require( (unsigned) data <= 0xFF );263264ggstereo_save = data;265266run_until( time );267268for ( int i = 0; i < osc_count; i++ )269{270Sms_Osc& osc = *oscs [i];271int flags = data >> i;272Blip_Buffer* old_output = osc.output;273osc.output_select = (flags >> 3 & 2) | (flags & 1);274osc.output = osc.outputs [osc.output_select];275if ( osc.output != old_output && osc.last_amp )276{277if ( old_output )278{279square_synth.offset( time, -osc.last_amp, old_output );280}281osc.last_amp = 0;282}283}284}285286// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )287static unsigned char const volumes [16] = {28864, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0289};290291void Sms_Apu::write_data( blip_time_t time, int data )292{293require( (unsigned) data <= 0xFF );294295run_until( time );296297if ( data & 0x80 )298latch = data;299300int index = (latch >> 5) & 3;301if ( latch & 0x10 )302{303oscs [index]->volume = volumes [data & 15];304}305else if ( index < 3 )306{307Sms_Square& sq = squares [index];308if ( data & 0x80 )309sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);310else311sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);312}313else314{315int select = data & 3;316if ( select < 3 )317noise.period = &noise_periods [select];318else319noise.period = &squares [2].period;320321noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;322noise.shifter = 0x8000;323}324}325326327