rockbox/apps/codecs/libgme/opl_apu.c
Andree Buschmann 631d22b8e5 4th part of FS#12176. Volume settings migrated to fixed point for libgme.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30278 a1c6a512-1295-4272-9138-f99709370657
2011-08-11 06:18:39 +00:00

198 lines
4.3 KiB
C

#include "opl_apu.h"
#include "blargg_source.h"
/* NOTE: Removed unused chips ~ gama */
blargg_err_t Opl_init( struct Opl_Apu* this, long clock, long rate, blip_time_t period, enum opl_type_t type )
{
Synth_init( &this->synth );
this->type_ = type;
this->clock_ = clock;
this->rate_ = rate;
this->period_ = period;
Opl_set_output( this, 0 );
Opl_volume( this, (int)FP_ONE_VOLUME );
switch (type)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
OPLL_new ( &this->opll, clock, rate );
OPLL_reset_patch( &this->opll, OPLL_2413_TONE );
break;
case type_vrc7:
OPLL_new ( &this->opll, clock, rate );
OPLL_reset_patch( &this->opll, OPLL_VRC7_TONE );
break;
case type_msxaudio:
OPL_init( &this->opl, this->opl_memory, sizeof this->opl_memory );
OPL_setSampleRate( &this->opl, rate, clock );
OPL_setInternalVolume(&this->opl, 1 << 13);
break;
}
Opl_reset( this );
return 0;
}
void Opl_shutdown( struct Opl_Apu* this )
{
switch (this->type_)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
case type_vrc7:
OPLL_delete( &this->opll );
break;
case type_msxaudio: break;
}
}
void Opl_reset( struct Opl_Apu* this )
{
this->addr = 0;
this->next_time = 0;
this->last_amp = 0;
switch (this->type_)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
case type_vrc7:
OPLL_reset( &this->opll );
OPLL_setMask( &this->opll, 0 );
break;
case type_msxaudio:
OPL_reset( &this->opl );
break;
}
}
static void run_until( struct Opl_Apu* this, blip_time_t end_time );
void Opl_write_data( struct Opl_Apu* this, blip_time_t time, int data )
{
run_until( this, time );
switch (this->type_)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
case type_vrc7:
OPLL_writeIO( &this->opll, 0, this->addr );
OPLL_writeIO( &this->opll, 1, data );
break;
case type_msxaudio:
OPL_writeReg( &this->opl, this->addr, data );
break;
}
}
int Opl_read( struct Opl_Apu* this, blip_time_t time, int port )
{
run_until( this, time );
switch (this->type_)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
case type_vrc7:
return OPLL_read( &this->opll, port );
case type_msxaudio:
return OPL_readStatus( &this->opl );
}
return 0;
}
void Opl_end_frame( struct Opl_Apu* this, blip_time_t time )
{
run_until( this, time );
this->next_time -= time;
if ( this->output_ )
Blip_set_modified( this->output_ );
}
static void run_until( struct Opl_Apu* this, blip_time_t end_time )
{
if ( end_time > this->next_time )
{
blip_time_t time_delta = end_time - this->next_time;
blip_time_t time = this->next_time;
unsigned count = time_delta / this->period_ + 1;
switch (this->type_)
{
case type_opll:
case type_msxmusic:
case type_smsfmunit:
case type_vrc7:
{
OPLL* opll = &this->opll; // cache
struct Blip_Buffer* const output = this->output_;
while ( count > 0 )
{
unsigned todo = count;
if ( todo > 1024 ) todo = 1024;
short *buffer = OPLL_update_buffer(opll, todo);
if ( output && buffer )
{
int last_amp = this->last_amp;
unsigned i;
for ( i = 0; i < todo; i++ )
{
int amp = buffer [i];
int delta = amp - last_amp;
if ( delta )
{
last_amp = amp;
Synth_offset_inline( &this->synth, time, delta, output );
}
time += this->period_;
}
this->last_amp = last_amp;
}
count -= todo;
}
}
break;
case type_msxaudio:
{
struct Y8950* opl = &this->opl;
struct Blip_Buffer* const output = this->output_;
while ( count > 0 )
{
unsigned todo = count;
if ( todo > 1024 ) todo = 1024;
int *buffer = OPL_updateBuffer(opl, todo);
if ( output && buffer )
{
int last_amp = this->last_amp;
unsigned i;
for ( i = 0; i < todo; i++ )
{
int amp = buffer [i];
int delta = amp - last_amp;
if ( delta )
{
last_amp = amp;
Synth_offset_inline( &this->synth, time, delta, output );
}
time += this->period_;
}
this->last_amp = last_amp;
}
count -= todo;
}
}
break;
}
this->next_time = time;
}
}