/***************************************************************************************1* Genesis Plus2* Sound Hardware3*4* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)5* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)6*7* Redistribution and use of this code or any derivative works are permitted8* provided that the following conditions are met:9*10* - Redistributions may not be sold, nor may they be used in a commercial11* product or activity.12*13* - Redistributions that are modified from the original source must include the14* complete source code, including the source code for all components used by a15* binary built from the modified sources. However, as a special exception, the16* source code distributed need not include anything that is normally distributed17* (in either source or binary form) with the major components (compiler, kernel,18* and so on) of the operating system on which the executable runs, unless that19* component itself accompanies the executable.20*21* - Redistributions must reproduce the above copyright notice, this list of22* conditions and the following disclaimer in the documentation and/or other23* materials provided with the distribution.24*25* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"26* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE27* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE28* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE29* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR30* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF31* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS32* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN33* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)34* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE35* POSSIBILITY OF SUCH DAMAGE.36*37****************************************************************************************/3839#include "shared.h"40#include "blip_buf.h"4142/* FM output buffer (large enough to hold a whole frame at original chips rate) */43int fm_buffer[1080 * 2];44int fm_last[2];45int *fm_ptr;4647/* Cycle-accurate FM samples */48uint32 fm_cycles_ratio;49uint32 fm_cycles_start;50uint32 fm_cycles_count;5152/* YM chip function pointers */53void (*YM_Reset)(void);54void (*YM_Update)(int *buffer, int length);55void (*YM_Write)(unsigned int a, unsigned int v);5657/* Run FM chip until required M-cycles */58INLINE void fm_update(unsigned int cycles)59{60if (cycles > fm_cycles_count)61{62/* number of samples to run */63unsigned int samples = (cycles - fm_cycles_count + fm_cycles_ratio - 1) / fm_cycles_ratio;6465/* run FM chip to sample buffer */66YM_Update(fm_ptr, samples);6768/* update FM buffer pointer */69fm_ptr += (samples << 1);7071/* update FM cycle counter */72fm_cycles_count += samples * fm_cycles_ratio;73}74}7576void sound_init( void )77{78/* Initialize FM chip */79if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)80{81/* YM2612 */82YM2612Init();83YM2612Config(config.dac_bits);84YM_Reset = YM2612ResetChip;85YM_Update = YM2612Update;86YM_Write = YM2612Write;8788/* chip is running a VCLK / 144 = MCLK / 7 / 144 */89fm_cycles_ratio = 144 * 7;90}91else92{93/* YM2413 */94YM2413Init();95YM_Reset = YM2413ResetChip;96YM_Update = YM2413Update;97YM_Write = YM2413Write;9899/* chip is running a ZCLK / 72 = MCLK / 15 / 72 */100fm_cycles_ratio = 72 * 15;101}102103/* Initialize PSG chip */104SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);105}106107void sound_reset(void)108{109/* reset sound chips */110YM_Reset();111SN76489_Reset();112113/* reset FM buffer ouput */114fm_last[0] = fm_last[1] = 0;115116/* reset FM buffer pointer */117fm_ptr = fm_buffer;118119/* reset FM cycle counters */120fm_cycles_start = fm_cycles_count = 0;121}122123int sound_update(unsigned int cycles)124{125int delta, preamp, time, l, r, *ptr;126127/* Run PSG & FM chips until end of frame */128SN76489_Update(cycles);129fm_update(cycles);130131/* FM output pre-amplification */132preamp = config.fm_preamp;133134/* FM frame initial timestamp */135time = fm_cycles_start;136137/* Restore last FM outputs from previous frame */138l = fm_last[0];139r = fm_last[1];140141/* FM buffer start pointer */142ptr = fm_buffer;143144/* flush FM samples */145if (config.hq_fm)146{147/* high-quality Band-Limited synthesis */148do149{150/* left channel */151delta = ((*ptr++ * preamp) / 100) - l;152l += delta;153blip_add_delta(snd.blips[0][0], time, delta);154155/* right channel */156delta = ((*ptr++ * preamp) / 100) - r;157r += delta;158blip_add_delta(snd.blips[0][1], time, delta);159160/* increment time counter */161time += fm_cycles_ratio;162}163while (time < cycles);164}165else166{167/* faster Linear Interpolation */168do169{170/* left channel */171delta = ((*ptr++ * preamp) / 100) - l;172l += delta;173blip_add_delta_fast(snd.blips[0][0], time, delta);174175/* right channel */176delta = ((*ptr++ * preamp) / 100) - r;177r += delta;178blip_add_delta_fast(snd.blips[0][1], time, delta);179180/* increment time counter */181time += fm_cycles_ratio;182}183while (time < cycles);184}185186/* reset FM buffer pointer */187fm_ptr = fm_buffer;188189/* save last FM output for next frame */190fm_last[0] = l;191fm_last[1] = r;192193/* adjust FM cycle counters for next frame */194fm_cycles_count = fm_cycles_start = time - cycles;195196/* end of blip buffers time frame */197blip_end_frame(snd.blips[0][0], cycles);198blip_end_frame(snd.blips[0][1], cycles);199200/* return number of available samples */201return blip_samples_avail(snd.blips[0][0]);202}203204int sound_context_save(uint8 *state)205{206int bufferptr = 0;207208if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)209{210bufferptr = YM2612SaveContext(state);211}212else213{214save_param(YM2413GetContextPtr(),YM2413GetContextSize());215}216217save_param(SN76489_GetContextPtr(),SN76489_GetContextSize());218219save_param(&fm_cycles_start,sizeof(fm_cycles_start));220221return bufferptr;222}223224int sound_context_load(uint8 *state)225{226int bufferptr = 0;227228if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)229{230bufferptr = YM2612LoadContext(state);231YM2612Config(config.dac_bits);232}233else234{235load_param(YM2413GetContextPtr(),YM2413GetContextSize());236}237238load_param(SN76489_GetContextPtr(),SN76489_GetContextSize());239240load_param(&fm_cycles_start,sizeof(fm_cycles_start));241fm_cycles_count = fm_cycles_start;242243return bufferptr;244}245246void fm_reset(unsigned int cycles)247{248/* synchronize FM chip with CPU */249fm_update(cycles);250251/* reset FM chip */252YM_Reset();253}254255void fm_write(unsigned int cycles, unsigned int address, unsigned int data)256{257/* synchronize FM chip with CPU (on data port write only) */258if (address & 1)259{260fm_update(cycles);261}262263/* write FM register */264YM_Write(address, data);265}266267unsigned int fm_read(unsigned int cycles, unsigned int address)268{269/* synchronize FM chip with CPU */270fm_update(cycles);271272/* read FM status (YM2612 only) */273return YM2612Read();274}275276277