238 lines
5.1 KiB
C++
238 lines
5.1 KiB
C++
// Game_Music_Emu $vers. http://www.slack.net/~ant/
|
|
|
|
#include "Vgm_Core.h"
|
|
|
|
#include "blargg_endian.h"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
/* 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"
|
|
|
|
Vgm_Core::Vgm_Core()
|
|
{
|
|
vgmp = (VGM_PLAYER *) VGMPlay_Init();
|
|
vgmp->VGMMaxLoop = 0;
|
|
VGMPlay_Init2(vgmp);
|
|
}
|
|
|
|
Vgm_Core::~Vgm_Core()
|
|
{
|
|
StopVGM(vgmp);
|
|
CloseVGMFile(vgmp);
|
|
VGMPlay_Deinit(vgmp);
|
|
}
|
|
|
|
static UINT32 gcd(UINT32 x, UINT32 y)
|
|
{
|
|
UINT32 shift;
|
|
UINT32 diff;
|
|
|
|
// Thanks to Wikipedia for this algorithm
|
|
// http://en.wikipedia.org/wiki/Binary_GCD_algorithm
|
|
if (! x || ! y)
|
|
return x | y;
|
|
|
|
for (shift = 0; ((x | y) & 1) == 0; shift ++)
|
|
{
|
|
x >>= 1;
|
|
y >>= 1;
|
|
}
|
|
|
|
while((x & 1) == 0)
|
|
x >>= 1;
|
|
|
|
do
|
|
{
|
|
while((y & 1) == 0)
|
|
y >>= 1;
|
|
|
|
if (x < y)
|
|
{
|
|
y -= x;
|
|
}
|
|
else
|
|
{
|
|
diff = x - y;
|
|
x = y;
|
|
y = diff;
|
|
}
|
|
y >>= 1;
|
|
} while(y);
|
|
|
|
return x << shift;
|
|
}
|
|
|
|
void Vgm_Core::set_tempo( double t )
|
|
{
|
|
if ( file_begin() )
|
|
{
|
|
int vgm_rate_unit = header().lngRate;
|
|
if (!vgm_rate_unit)
|
|
vgm_rate_unit = 44100;
|
|
int vgm_rate = (int) (vgm_rate_unit * t + 0.5);
|
|
int old_rate = vgmp->VGMPbRate;
|
|
vgmp->VGMPbRate = vgm_rate;
|
|
vgmp->SampleRate = sample_rate;
|
|
|
|
if (vgmp->PlayingMode != 0xFF)
|
|
{
|
|
if (!old_rate)
|
|
old_rate = vgm_rate_unit;
|
|
|
|
INT32 TempSLng = gcd(vgm_rate_unit, vgmp->VGMPbRate);
|
|
vgmp->VGMPbRateMul = vgm_rate_unit / TempSLng;
|
|
vgmp->VGMPbRateDiv = vgmp->VGMPbRate / TempSLng;
|
|
|
|
vgmp->VGMSmplRateMul = vgmp->SampleRate * vgmp->VGMPbRateMul;
|
|
vgmp->VGMSmplRateDiv = vgmp->VGMSampleRate * vgmp->VGMPbRateDiv;
|
|
// same as above - to speed up the VGM <-> Playback calculation
|
|
TempSLng = gcd(vgmp->VGMSmplRateMul, vgmp->VGMSmplRateDiv);
|
|
vgmp->VGMSmplRateMul /= TempSLng;
|
|
vgmp->VGMSmplRateDiv /= TempSLng;
|
|
|
|
vgmp->VGMSmplPlayed = (INT32)((INT64)vgmp->VGMSmplPlayed * old_rate / vgm_rate);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct VGM_FILE_mem
|
|
{
|
|
VGM_FILE vf;
|
|
const BOOST::uint8_t* buffer;
|
|
UINT32 ptr;
|
|
UINT32 size;
|
|
};
|
|
|
|
static int VGMF_mem_Read(VGM_FILE* f, void* out, UINT32 count)
|
|
{
|
|
VGM_FILE_mem* mf = (VGM_FILE_mem *) f;
|
|
if (count + mf->ptr > mf->size)
|
|
count = mf->size - mf->ptr;
|
|
memcpy(out, mf->buffer + mf->ptr, count);
|
|
mf->ptr += count;
|
|
return count;
|
|
}
|
|
|
|
static int VGMF_mem_Seek(VGM_FILE* f, UINT32 offset)
|
|
{
|
|
VGM_FILE_mem* mf = (VGM_FILE_mem *) f;
|
|
if (offset > mf->size)
|
|
offset = mf->size;
|
|
mf->ptr = offset;
|
|
return 0;
|
|
}
|
|
|
|
static UINT32 VGMF_mem_GetSize(VGM_FILE* f)
|
|
{
|
|
VGM_FILE_mem* mf = (VGM_FILE_mem *) f;
|
|
return mf->size;
|
|
}
|
|
|
|
static UINT32 VGMF_mem_Tell(VGM_FILE* f)
|
|
{
|
|
VGM_FILE_mem* mf = (VGM_FILE_mem *) f;
|
|
return mf->ptr;
|
|
}
|
|
|
|
blargg_err_t Vgm_Core::load_mem_( byte const data [], int size )
|
|
{
|
|
VGM_FILE_mem memFile;
|
|
|
|
memFile.vf.Read = &VGMF_mem_Read;
|
|
memFile.vf.Seek = &VGMF_mem_Seek;
|
|
memFile.vf.GetSize = &VGMF_mem_GetSize;
|
|
memFile.vf.Tell = &VGMF_mem_Tell;
|
|
memFile.buffer = data;
|
|
memFile.ptr = 0;
|
|
memFile.size = size;
|
|
|
|
if ( !GetVGMFileInfo_Handle( (VGM_FILE *) &memFile, &_header, 0 ) )
|
|
return blargg_err_file_type;
|
|
|
|
memFile.ptr = 0;
|
|
|
|
if ( !OpenVGMFile_Handle( vgmp, (VGM_FILE *) &memFile ) )
|
|
return blargg_err_file_type;
|
|
|
|
if ( !header().lngLoopOffset )
|
|
vgmp->VGMMaxLoop = 1;
|
|
|
|
set_tempo( 1 );
|
|
|
|
return blargg_ok;
|
|
}
|
|
|
|
int Vgm_Core::get_channel_count()
|
|
{
|
|
// XXX may support more than this, but 32 bit masks and all...
|
|
unsigned i;
|
|
UINT32 j;
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
if (!GetAccurateChipNameByChannel(vgmp, i, &j))
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
char* Vgm_Core::get_voice_name(int channel)
|
|
{
|
|
UINT32 realChannel;
|
|
const char * name = GetAccurateChipNameByChannel(vgmp, channel, &realChannel);
|
|
size_t length = strlen(name) + 16;
|
|
char * finalName = (char *) malloc(length);
|
|
if (finalName)
|
|
#ifdef _MSC_VER
|
|
sprintf_s(finalName, length, "%s #%u", name, realChannel);
|
|
#else
|
|
sprintf(finalName, "%s #%u", name, realChannel);
|
|
#endif
|
|
return finalName;
|
|
}
|
|
|
|
void Vgm_Core::free_voice_name(char *name)
|
|
{
|
|
free(name);
|
|
}
|
|
|
|
void Vgm_Core::set_mute(int mask)
|
|
{
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
SetChannelMute(vgmp, i, (mask >> i) & 1);
|
|
}
|
|
}
|
|
|
|
void Vgm_Core::start_track()
|
|
{
|
|
PlayVGM(vgmp);
|
|
RestartVGM(vgmp);
|
|
}
|
|
|
|
int Vgm_Core::play_( int sample_count, short out [] )
|
|
{
|
|
// to do: timing is working mostly by luck
|
|
int pairs = (unsigned) sample_count / 2;
|
|
|
|
memset(out, 0, sizeof(short) * pairs * 2);
|
|
FillBuffer(vgmp, (WAVE_16BS*) out, pairs);
|
|
|
|
return pairs * 2;
|
|
}
|
|
|
|
void Vgm_Core::skip_( int count )
|
|
{
|
|
SeekVGM( vgmp, true, count / 2 );
|
|
}
|