cog/Frameworks/GME/gme/Nsf_Emu.cpp

342 lines
9.5 KiB
C++

// Game_Music_Emu $vers. http://www.slack.net/~ant/
#include "Nsf_Emu.h"
#if !NSF_EMU_APU_ONLY
#include "Nes_Namco_Apu.h"
#include "Nes_Vrc6_Apu.h"
#include "Nes_Fme7_Apu.h"
#include "Nes_Fds_Apu.h"
#include "Nes_Mmc5_Apu.h"
#include "Nes_Vrc7_Apu.h"
#endif
/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80, 0,0,0,0,0,0,0,0 };
Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80, 0,0,0,0,0,0,0,0 };
Nsf_Emu::Nsf_Emu()
{
set_type( gme_nsf_type );
set_silence_lookahead( 6 );
set_gain( 1.4 );
set_equalizer( nes_eq );
}
Nsf_Emu::~Nsf_Emu()
{
unload();
}
void Nsf_Emu::unload()
{
core_.unload();
Music_Emu::unload();
}
// Track info
static void copy_nsf_fields( Nsf_Emu::header_t const& h, track_info_t* out )
{
GME_COPY_FIELD( h, out, game );
GME_COPY_FIELD( h, out, author );
GME_COPY_FIELD( h, out, copyright );
if ( h.chip_flags )
Music_Emu::copy_field_( out->system, "Famicom" );
}
void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out )
{
out.hash_( &h.vers, sizeof(h.vers) );
out.hash_( &h.track_count, sizeof(h.track_count) );
out.hash_( &h.first_track, sizeof(h.first_track) );
out.hash_( &h.load_addr[0], sizeof(h.load_addr) );
out.hash_( &h.init_addr[0], sizeof(h.init_addr) );
out.hash_( &h.play_addr[0], sizeof(h.play_addr) );
out.hash_( &h.ntsc_speed[0], sizeof(h.ntsc_speed) );
out.hash_( &h.banks[0], sizeof(h.banks) );
out.hash_( &h.pal_speed[0], sizeof(h.pal_speed) );
out.hash_( &h.speed_flags, sizeof(h.speed_flags) );
out.hash_( &h.chip_flags, sizeof(h.chip_flags) );
out.hash_( &h.unused[0], sizeof(h.unused) );
out.hash_( data, data_size );
}
blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const
{
copy_nsf_fields( header(), out );
return blargg_ok;
}
static blargg_err_t check_nsf_header( Nsf_Emu::header_t const& h )
{
if ( !h.valid_tag() )
return blargg_err_file_type;
return blargg_ok;
}
struct Nsf_File : Gme_Info_
{
Nsf_Emu::header_t const* h;
Nsf_File() { set_type( gme_nsf_type ); }
blargg_err_t load_mem_( byte const begin [], int size )
{
h = ( Nsf_Emu::header_t const* ) begin;
if ( h->vers != 1 )
set_warning( "Unknown file version" );
int unsupported_chips = ~Nsf_Core::chips_mask;
#if NSF_EMU_NO_VRC7
unsupported_chips |= Nsf_Emu::header_t::vrc7_mask;
#endif
if ( h->chip_flags & unsupported_chips )
set_warning( "Uses unsupported audio expansion hardware" );
set_track_count( h->track_count );
return check_nsf_header( *h );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_nsf_fields( *h, out );
return blargg_ok;
}
blargg_err_t hash_( Hash_Function& out ) const
{
hash_nsf_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out );
return blargg_ok;
}
};
static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; }
static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; }
gme_type_t_ const gme_nsf_type [1] = {{ "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }};
// Setup
void Nsf_Emu::set_tempo_( double t )
{
core_.set_tempo( t );
}
void Nsf_Emu::append_voices( const char* const names [], int const types [], int count )
{
assert( voice_count_ + count < max_voices );
for ( int i = 0; i < count; i++ )
{
voice_names_ [voice_count_ + i] = names [i];
voice_types_ [voice_count_ + i] = types [i];
}
voice_count_ += count;
set_voice_count( voice_count_ );
set_voice_types( voice_types_ );
}
blargg_err_t Nsf_Emu::init_sound()
{
voice_count_ = 0;
set_voice_names( voice_names_ );
{
int const count = Nes_Apu::osc_count;
static const char* const names [Nes_Apu::osc_count] = {
"Square 1", "Square 2", "Triangle", "Noise", "DMC"
};
static int const types [count] = {
wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1
};
append_voices( names, types, count );
}
// Make adjusted_gain * 0.75 = 1.0 so usual APU and one sound chip uses 1.0
double adjusted_gain = 1.0 / 0.75 * gain();
#if !NSF_EMU_APU_ONLY
// TODO: order of chips here must match that in set_voice()
if ( core_.vrc6_apu() )
{
int const count = Nes_Vrc6_Apu::osc_count;
static const char* const names [count] = {
"Square 3", "Square 4", "Saw Wave"
};
static int const types [count] = {
wave_type+3, wave_type+4, wave_type+5,
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.fme7_apu() )
{
int const count = Nes_Fme7_Apu::osc_count;
static const char* const names [count] = {
"Square 3", "Square 4", "Square 5"
};
static int const types [count] = {
wave_type+3, wave_type+4, wave_type+5,
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.mmc5_apu() )
{
int const count = Nes_Mmc5_Apu::osc_count;
static const char* const names [count] = {
"Square 3", "Square 4", "PCM"
};
static int const types [count] = {
wave_type+3, wave_type+4, mixed_type+2
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.fds_apu() )
{
int const count = Nes_Fds_Apu::osc_count;
static const char* const names [count] = {
"FM"
};
static int const types [count] = {
wave_type+0
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.namco_apu() )
{
int const count = Nes_Namco_Apu::osc_count;
static const char* const names [count] = {
"Wave 1", "Wave 2", "Wave 3", "Wave 4",
"Wave 5", "Wave 6", "Wave 7", "Wave 8"
};
static int const types [count] = {
wave_type+3, wave_type+4, wave_type+5, wave_type+ 6,
wave_type+7, wave_type+8, wave_type+9, wave_type+10,
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.vrc7_apu() )
{
int const count = Nes_Vrc7_Apu::osc_count;
static const char* const names [count] = {
"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"
};
static int const types [count] = {
wave_type+3, wave_type+4, wave_type+5, wave_type+6,
wave_type+7, wave_type+8
};
append_voices( names, types, count );
adjusted_gain *= 0.75;
}
if ( core_.vrc7_apu() ) core_.vrc7_apu() ->volume( adjusted_gain );
if ( core_.namco_apu() ) core_.namco_apu()->volume( adjusted_gain );
if ( core_.vrc6_apu() ) core_.vrc6_apu() ->volume( adjusted_gain );
if ( core_.fme7_apu() ) core_.fme7_apu() ->volume( adjusted_gain );
if ( core_.mmc5_apu() ) core_.mmc5_apu() ->volume( adjusted_gain );
if ( core_.fds_apu() ) core_.fds_apu() ->volume( adjusted_gain );
#endif
if ( adjusted_gain > gain() )
adjusted_gain = gain(); // only occurs if no other sound chips
core_.nes_apu()->volume( adjusted_gain );
return blargg_ok;
}
blargg_err_t Nsf_Emu::load_( Data_Reader& in )
{
RETURN_ERR( core_.load( in ) );
set_track_count( header().track_count );
RETURN_ERR( check_nsf_header( header() ) );
set_warning( core_.warning() );
RETURN_ERR( init_sound() );
set_tempo( tempo() );
return setup_buffer( (int) (header().clock_rate() + 0.5) );
}
void Nsf_Emu::update_eq( blip_eq_t const& eq )
{
core_.nes_apu()->treble_eq( eq );
#if !NSF_EMU_APU_ONLY
{
if ( core_.namco_apu() ) core_.namco_apu()->treble_eq( eq );
if ( core_.vrc6_apu() ) core_.vrc6_apu() ->treble_eq( eq );
if ( core_.fme7_apu() ) core_.fme7_apu() ->treble_eq( eq );
if ( core_.mmc5_apu() ) core_.mmc5_apu() ->treble_eq( eq );
if ( core_.fds_apu() ) core_.fds_apu() ->treble_eq( eq );
if ( core_.vrc7_apu() ) core_.vrc7_apu() ->treble_eq( eq );
}
#endif
}
void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
{
#define HANDLE_CHIP( chip ) \
if ( chip && (i -= chip->osc_count) < 0 )\
{\
chip->set_output( i + chip->osc_count, buf );\
return;\
}\
HANDLE_CHIP( core_.nes_apu() );
#if !NSF_EMU_APU_ONLY
{
// TODO: order of chips here must match that in init_sound()
HANDLE_CHIP( core_.vrc6_apu() );
HANDLE_CHIP( core_.fme7_apu() );
HANDLE_CHIP( core_.mmc5_apu() );
HANDLE_CHIP( core_.fds_apu() );
HANDLE_CHIP( core_.namco_apu() );
HANDLE_CHIP( core_.vrc7_apu() );
}
#endif
}
blargg_err_t Nsf_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
return core_.start_track( track );
}
blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
{
core_.end_frame( duration );
const char* w = core_.warning();
if ( w )
set_warning( w );
return blargg_ok;
}
blargg_err_t Nsf_Emu::hash_( Hash_Function& out ) const
{
hash_nsf_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out );
return blargg_ok;
}