mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-29 23:41:48 +00:00
- cleaned up the dependencies of the OPL interface layer.
This also removes the OPL dumper because I wasn't able to get any non-broken output from it and have no desire to fix such a niche feature.
This commit is contained in:
parent
17eac1c57b
commit
b085ac3efb
10 changed files with 308 additions and 529 deletions
|
@ -1191,7 +1191,6 @@ set (PCH_SOURCES
|
||||||
sound/music/i_soundfont.cpp
|
sound/music/i_soundfont.cpp
|
||||||
sound/backend/i_sound.cpp
|
sound/backend/i_sound.cpp
|
||||||
sound/mididevices/music_adlmidi_mididevice.cpp
|
sound/mididevices/music_adlmidi_mididevice.cpp
|
||||||
sound/mididevices/music_opldumper_mididevice.cpp
|
|
||||||
sound/mididevices/music_opl_mididevice.cpp
|
sound/mididevices/music_opl_mididevice.cpp
|
||||||
sound/mididevices/music_opnmidi_mididevice.cpp
|
sound/mididevices/music_opnmidi_mididevice.cpp
|
||||||
sound/mididevices/music_timiditypp_mididevice.cpp
|
sound/mididevices/music_timiditypp_mididevice.cpp
|
||||||
|
|
|
@ -84,7 +84,7 @@ int getOPLCore(const char* args)
|
||||||
|
|
||||||
|
|
||||||
OPLMIDIDevice::OPLMIDIDevice(const char *args)
|
OPLMIDIDevice::OPLMIDIDevice(const char *args)
|
||||||
: SoftSynthMIDIDevice((int)OPL_SAMPLE_RATE), OPLmusicBlock(getOPLCore(args))
|
: SoftSynthMIDIDevice((int)OPL_SAMPLE_RATE), OPLmusicBlock(getOPLCore(args), opl_numchips)
|
||||||
{
|
{
|
||||||
FullPan = opl_fullpan;
|
FullPan = opl_fullpan;
|
||||||
auto lump = Wads.CheckNumForName("GENMIDI", ns_global);
|
auto lump = Wads.CheckNumForName("GENMIDI", ns_global);
|
||||||
|
|
|
@ -1,385 +0,0 @@
|
||||||
/*
|
|
||||||
** music_opl_mididevice.cpp
|
|
||||||
** Writes raw OPL commands from the emulated OPL MIDI output to disk.
|
|
||||||
**
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
** Copyright 2008 Randy Heit
|
|
||||||
** All rights reserved.
|
|
||||||
**
|
|
||||||
** Redistribution and use in source and binary forms, with or without
|
|
||||||
** modification, are permitted provided that the following conditions
|
|
||||||
** are met:
|
|
||||||
**
|
|
||||||
** 1. Redistributions of source code must retain the above copyright
|
|
||||||
** notice, this list of conditions and the following disclaimer.
|
|
||||||
** 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
** notice, this list of conditions and the following disclaimer in the
|
|
||||||
** documentation and/or other materials provided with the distribution.
|
|
||||||
** 3. The name of the author may not be used to endorse or promote products
|
|
||||||
** derived from this software without specific prior written permission.
|
|
||||||
**
|
|
||||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
**---------------------------------------------------------------------------
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
// HEADER FILES ------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "i_musicinterns.h"
|
|
||||||
#include "templates.h"
|
|
||||||
#include "opl.h"
|
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
|
||||||
|
|
||||||
// TYPES -------------------------------------------------------------------
|
|
||||||
|
|
||||||
class OPLDump : public OPLEmul
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OPLDump(FileWriter *file) : File(file), TimePerTick(0), CurTime(0),
|
|
||||||
CurIntTime(0), TickMul(1), CurChip(0) {}
|
|
||||||
|
|
||||||
// If we're doing things right, these should never be reset.
|
|
||||||
virtual void Reset() { assert(0); }
|
|
||||||
|
|
||||||
// Update() is only used for getting waveform data, which dumps don't do.
|
|
||||||
virtual void Update(float *buffer, int length) { assert(0); }
|
|
||||||
|
|
||||||
// OPL dumps don't pan beyond what OPL3 is capable of (which is
|
|
||||||
// already written using registers from the original data).
|
|
||||||
virtual void SetPanning(int c, float left, float right) {}
|
|
||||||
|
|
||||||
// Only for the OPL dumpers, not the emulators
|
|
||||||
virtual void SetClockRate(double samples_per_tick) {}
|
|
||||||
virtual void WriteDelay(int ticks) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
FileWriter *File;
|
|
||||||
double TimePerTick; // in milliseconds
|
|
||||||
double CurTime;
|
|
||||||
int CurIntTime;
|
|
||||||
int TickMul;
|
|
||||||
uint8_t CurChip;
|
|
||||||
};
|
|
||||||
|
|
||||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
||||||
|
|
||||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
||||||
|
|
||||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
||||||
|
|
||||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
||||||
|
|
||||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
||||||
|
|
||||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
||||||
|
|
||||||
// CODE --------------------------------------------------------------------
|
|
||||||
|
|
||||||
class OPL_RDOSdump : public OPLDump
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OPL_RDOSdump(FileWriter *file) : OPLDump(file)
|
|
||||||
{
|
|
||||||
assert(File != NULL);
|
|
||||||
file->Write("RAWADATA\0", 10);
|
|
||||||
NeedClockRate = true;
|
|
||||||
}
|
|
||||||
virtual ~OPL_RDOSdump()
|
|
||||||
{
|
|
||||||
if (File != NULL)
|
|
||||||
{
|
|
||||||
uint16_t endmark = 0xFFFF;
|
|
||||||
File->Write(&endmark, 2);
|
|
||||||
delete File;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void WriteReg(int reg, int v)
|
|
||||||
{
|
|
||||||
assert(File != NULL);
|
|
||||||
uint8_t chipnum = reg >> 8;
|
|
||||||
if (chipnum != CurChip)
|
|
||||||
{
|
|
||||||
uint8_t switcher[2] = { (uint8_t)(chipnum + 1), 2 };
|
|
||||||
File->Write(switcher, 2);
|
|
||||||
}
|
|
||||||
reg &= 255;
|
|
||||||
if (reg != 0 && reg != 2 && (reg != 255 || v != 255))
|
|
||||||
{
|
|
||||||
uint8_t cmd[2] = { uint8_t(v), uint8_t(reg) };
|
|
||||||
File->Write(cmd, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SetClockRate(double samples_per_tick)
|
|
||||||
{
|
|
||||||
TimePerTick = samples_per_tick / OPL_SAMPLE_RATE * 1000.0;
|
|
||||||
|
|
||||||
double clock_rate;
|
|
||||||
int clock_mul;
|
|
||||||
uint16_t clock_word;
|
|
||||||
|
|
||||||
clock_rate = samples_per_tick * ADLIB_CLOCK_MUL;
|
|
||||||
clock_mul = 1;
|
|
||||||
|
|
||||||
// The RDos raw format's clock rate is stored in a word. Therefore,
|
|
||||||
// the longest tick that can be stored is only ~55 ms.
|
|
||||||
while (clock_rate / clock_mul + 0.5 > 65535.0)
|
|
||||||
{
|
|
||||||
clock_mul++;
|
|
||||||
}
|
|
||||||
clock_word = uint16_t(clock_rate / clock_mul + 0.5);
|
|
||||||
|
|
||||||
if (NeedClockRate)
|
|
||||||
{ // Set the initial clock rate.
|
|
||||||
clock_word = LittleShort(clock_word);
|
|
||||||
File->Seek(8, SEEK_SET);
|
|
||||||
File->Write(&clock_word, 2);
|
|
||||||
File->Seek(0, SEEK_END);
|
|
||||||
NeedClockRate = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // Change the clock rate in the middle of the song.
|
|
||||||
uint8_t clock_change[4] = { 0, 2, uint8_t(clock_word & 255), uint8_t(clock_word >> 8) };
|
|
||||||
File->Write(clock_change, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void WriteDelay(int ticks)
|
|
||||||
{
|
|
||||||
if (ticks > 0)
|
|
||||||
{ // RDos raw has very precise delays but isn't very efficient at
|
|
||||||
// storing long delays.
|
|
||||||
uint8_t delay[2];
|
|
||||||
|
|
||||||
ticks *= TickMul;
|
|
||||||
delay[1] = 0;
|
|
||||||
while (ticks > 255)
|
|
||||||
{
|
|
||||||
ticks -= 255;
|
|
||||||
delay[0] = 255;
|
|
||||||
File->Write(delay, 2);
|
|
||||||
}
|
|
||||||
delay[0] = uint8_t(ticks);
|
|
||||||
File->Write(delay, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
bool NeedClockRate;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OPL_DOSBOXdump : public OPLDump
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OPL_DOSBOXdump(FileWriter *file, bool dual) : OPLDump(file), Dual(dual)
|
|
||||||
{
|
|
||||||
assert(File != NULL);
|
|
||||||
File->Write("DBRAWOPL"
|
|
||||||
"\0\0" // Minor version number
|
|
||||||
"\1\0" // Major version number
|
|
||||||
"\0\0\0\0" // Total milliseconds
|
|
||||||
"\0\0\0", // Total data
|
|
||||||
20);
|
|
||||||
char type[4] = { (char)(Dual * 2), 0, 0, 0 }; // Single or dual OPL-2
|
|
||||||
File->Write(type, 4);
|
|
||||||
}
|
|
||||||
virtual ~OPL_DOSBOXdump()
|
|
||||||
{
|
|
||||||
if (File != NULL)
|
|
||||||
{
|
|
||||||
long where_am_i =File->Tell();
|
|
||||||
uint32_t len[2];
|
|
||||||
|
|
||||||
File->Seek(12, SEEK_SET);
|
|
||||||
len[0] = LittleLong(CurIntTime);
|
|
||||||
len[1] = LittleLong(uint32_t(where_am_i - 24));
|
|
||||||
File->Write(len, 8);
|
|
||||||
delete File;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void WriteReg(int reg, int v)
|
|
||||||
{
|
|
||||||
assert(File != NULL);
|
|
||||||
uint8_t chipnum = reg >> 8;
|
|
||||||
if (chipnum != CurChip)
|
|
||||||
{
|
|
||||||
CurChip = chipnum;
|
|
||||||
chipnum += 2;
|
|
||||||
File->Write(&chipnum, 1);
|
|
||||||
}
|
|
||||||
reg &= 255;
|
|
||||||
uint8_t cmd[3] = { 4, uint8_t(reg), uint8_t(v) };
|
|
||||||
File->Write(cmd + (reg > 4), 3 - (reg > 4));
|
|
||||||
}
|
|
||||||
virtual void WriteDelay(int ticks)
|
|
||||||
{
|
|
||||||
if (ticks > 0)
|
|
||||||
{ // DosBox only has millisecond-precise delays.
|
|
||||||
int delay;
|
|
||||||
|
|
||||||
CurTime += TimePerTick * ticks;
|
|
||||||
delay = int(CurTime + 0.5) - CurIntTime;
|
|
||||||
CurIntTime += delay;
|
|
||||||
while (delay > 65536)
|
|
||||||
{
|
|
||||||
uint8_t cmd[3] = { 1, 255, 255 };
|
|
||||||
File->Write(cmd, 2);
|
|
||||||
delay -= 65536;
|
|
||||||
}
|
|
||||||
delay--;
|
|
||||||
if (delay <= 255)
|
|
||||||
{
|
|
||||||
uint8_t cmd[2] = { 0, uint8_t(delay) };
|
|
||||||
File->Write(cmd, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(delay <= 65535);
|
|
||||||
uint8_t cmd[3] = { 1, uint8_t(delay & 255), uint8_t(delay >> 8) };
|
|
||||||
File->Write(cmd, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
bool Dual;
|
|
||||||
};
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// OPLDumperMIDIDevice Constructor
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename)
|
|
||||||
: OPLMIDIDevice(NULL)
|
|
||||||
{
|
|
||||||
// Replace the standard OPL device with a disk writer.
|
|
||||||
delete io;
|
|
||||||
io = new DiskWriterIO(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// OPLDumperMIDIDevice Destructor
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
OPLDumperMIDIDevice::~OPLDumperMIDIDevice()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// OPLDumperMIDIDevice :: Resume
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int OPLDumperMIDIDevice::Resume()
|
|
||||||
{
|
|
||||||
int time;
|
|
||||||
|
|
||||||
time = PlayTick();
|
|
||||||
while (time != 0)
|
|
||||||
{
|
|
||||||
io->WriteDelay(time);
|
|
||||||
time = PlayTick();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// OPLDumperMIDIDevice :: Stop
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void OPLDumperMIDIDevice::Stop()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// DiskWriterIO Constructor
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
DiskWriterIO::DiskWriterIO(const char *filename)
|
|
||||||
: Filename(filename)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// DiskWriterIO Destructor
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
DiskWriterIO::~DiskWriterIO()
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// DiskWriterIO :: Init
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
int DiskWriterIO::Init(uint32_t numchips, bool, bool initopl3)
|
|
||||||
{
|
|
||||||
FileWriter *file = FileWriter::Open(Filename);
|
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
Printf("Could not open %s for writing.\n", Filename.GetChars());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
numchips = clamp(numchips, 1u, 2u);
|
|
||||||
memset(chips, 0, sizeof(chips));
|
|
||||||
// If the file extension is unknown or not present, the default format
|
|
||||||
// is RAW. Otherwise, you can use DRO.
|
|
||||||
if (Filename.Len() < 5 || stricmp(&Filename[Filename.Len() - 4], ".dro") != 0)
|
|
||||||
{
|
|
||||||
chips[0] = new OPL_RDOSdump(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chips[0] = new OPL_DOSBOXdump(file, numchips > 1);
|
|
||||||
}
|
|
||||||
NumChannels = OPL_NUM_VOICES * numchips;
|
|
||||||
NumChips = numchips;
|
|
||||||
IsOPL3 = numchips > 1;
|
|
||||||
WriteInitState(initopl3);
|
|
||||||
return numchips;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// DiskWriterIO :: SetClockRate
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void DiskWriterIO::SetClockRate(double samples_per_tick)
|
|
||||||
{
|
|
||||||
static_cast<OPLDump *>(chips[0])->SetClockRate(samples_per_tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// DiskWriterIO :: WriteDelay
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void DiskWriterIO :: WriteDelay(int ticks)
|
|
||||||
{
|
|
||||||
static_cast<OPLDump *>(chips[0])->WriteDelay(ticks);
|
|
||||||
}
|
|
|
@ -694,39 +694,6 @@ static MIDISource *GetMIDISource(const char *fn)
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// CCMD writeopl
|
|
||||||
//
|
|
||||||
// If the current song can be played with OPL instruments, dump it to
|
|
||||||
// the specified file on disk.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
UNSAFE_CCMD (writeopl)
|
|
||||||
{
|
|
||||||
if (argv.argc() >= 3 && argv.argc() <= 7)
|
|
||||||
{
|
|
||||||
auto source = GetMIDISource(argv[1]);
|
|
||||||
if (source == nullptr) return;
|
|
||||||
|
|
||||||
// We must stop the currently playing music to avoid interference between two synths.
|
|
||||||
auto savedsong = mus_playing;
|
|
||||||
S_StopMusic(true);
|
|
||||||
auto streamer = new MIDIStreamer(MDEV_OPL, nullptr);
|
|
||||||
streamer->SetMIDISource(source);
|
|
||||||
streamer->DumpOPL(argv[2], argv.argc() <4 ? 0 : (int)strtol(argv[3], nullptr, 10));
|
|
||||||
delete streamer;
|
|
||||||
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Printf("Usage: writeopl <midi> <filename> [subsong]\n"
|
|
||||||
" - use '*' as song name to dump the currently playing song\n"
|
|
||||||
" - use 0 for subsong to play the default\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
|
|
@ -140,18 +140,6 @@ protected:
|
||||||
|
|
||||||
// OPL dumper implementation of a MIDI output device ------------------------
|
// OPL dumper implementation of a MIDI output device ------------------------
|
||||||
|
|
||||||
struct DiskWriterIO : public OPLio
|
|
||||||
{
|
|
||||||
DiskWriterIO(const char* filename);
|
|
||||||
~DiskWriterIO();
|
|
||||||
|
|
||||||
int Init(uint32_t numchips, bool notused, bool initopl3);
|
|
||||||
void SetClockRate(double samples_per_tick);
|
|
||||||
void WriteDelay(int ticks);
|
|
||||||
|
|
||||||
FString Filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OPLDumperMIDIDevice : public OPLMIDIDevice
|
class OPLDumperMIDIDevice : public OPLMIDIDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -226,7 +214,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DumpWave(const char *filename, int subsong, int samplerate);
|
bool DumpWave(const char *filename, int subsong, int samplerate);
|
||||||
bool DumpOPL(const char *filename, int subsong);
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -313,23 +313,6 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
bool MIDIStreamer::DumpOPL(const char *filename, int subsong)
|
|
||||||
{
|
|
||||||
m_Looping = false;
|
|
||||||
if (source == nullptr) return false; // We have nothing to play so abort.
|
|
||||||
source->SetMIDISubsong(subsong);
|
|
||||||
|
|
||||||
assert(MIDI == NULL);
|
|
||||||
MIDI = new OPLDumperMIDIDevice(filename);
|
|
||||||
return InitPlayback();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// MIDIStreamer :: DumpWave
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate)
|
bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate)
|
||||||
{
|
{
|
||||||
m_Looping = false;
|
m_Looping = false;
|
||||||
|
|
|
@ -65,9 +65,17 @@ int getOPLCore(const char* args);
|
||||||
OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args)
|
OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args)
|
||||||
{
|
{
|
||||||
int samples = int(OPL_SAMPLE_RATE / 14);
|
int samples = int(OPL_SAMPLE_RATE / 14);
|
||||||
|
const char* error;
|
||||||
|
|
||||||
int core = getOPLCore(args);
|
int core = getOPLCore(args);
|
||||||
Music = new OPLmusicFile (reader, core);
|
auto data = reader.Read();
|
||||||
|
Music = new OPLmusicFile (data.Data(), data.Size(), core, opl_numchips, error);
|
||||||
|
if (!Music->IsValid())
|
||||||
|
{
|
||||||
|
Printf(PRINT_BOLD, "%s", error? error : "Invalid OPL format\n");
|
||||||
|
delete Music;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_Stream = GSnd->CreateStream (FillStream, samples*4,
|
m_Stream = GSnd->CreateStream (FillStream, samples*4,
|
||||||
(core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
|
(core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this);
|
||||||
|
@ -97,7 +105,7 @@ bool OPLMUSSong::IsValid () const
|
||||||
|
|
||||||
void OPLMUSSong::ResetChips ()
|
void OPLMUSSong::ResetChips ()
|
||||||
{
|
{
|
||||||
Music->ResetChips ();
|
Music->ResetChips (opl_numchips);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OPLMUSSong::IsPlaying ()
|
bool OPLMUSSong::IsPlaying ()
|
||||||
|
|
255
src/sound/oplsynth/o_swap.h
Normal file
255
src/sound/oplsynth/o_swap.h
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// Endianess handling, swapping 16bit and 32bit.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __M_SWAP_H__
|
||||||
|
#define __M_SWAP_H__
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Endianess handling.
|
||||||
|
// WAD files are stored little endian.
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <libkern/OSByteOrder.h>
|
||||||
|
|
||||||
|
inline short LittleShort(short x)
|
||||||
|
{
|
||||||
|
return (short)OSSwapLittleToHostInt16((uint16_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short LittleShort(unsigned short x)
|
||||||
|
{
|
||||||
|
return OSSwapLittleToHostInt16(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline short LittleShort(int x)
|
||||||
|
{
|
||||||
|
return OSSwapLittleToHostInt16((uint16_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short LittleShort(unsigned int x)
|
||||||
|
{
|
||||||
|
return OSSwapLittleToHostInt16((uint16_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int LittleLong(int x)
|
||||||
|
{
|
||||||
|
return OSSwapLittleToHostInt32((uint32_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int LittleLong(unsigned int x)
|
||||||
|
{
|
||||||
|
return OSSwapLittleToHostInt32(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline short BigShort(short x)
|
||||||
|
{
|
||||||
|
return (short)OSSwapBigToHostInt16((uint16_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short BigShort(unsigned short x)
|
||||||
|
{
|
||||||
|
return OSSwapBigToHostInt16(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int BigLong(int x)
|
||||||
|
{
|
||||||
|
return OSSwapBigToHostInt32((uint32_t)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int BigLong(unsigned int x)
|
||||||
|
{
|
||||||
|
return OSSwapBigToHostInt32(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined __BIG_ENDIAN__
|
||||||
|
|
||||||
|
// Swap 16bit, that is, MSB and LSB byte.
|
||||||
|
// No masking with 0xFF should be necessary.
|
||||||
|
inline short LittleShort (short x)
|
||||||
|
{
|
||||||
|
return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short LittleShort (unsigned short x)
|
||||||
|
{
|
||||||
|
return (unsigned short)((x>>8) | (x<<8));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline short LittleShort (int x)
|
||||||
|
{
|
||||||
|
return LittleShort((short)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short LittleShort (unsigned int x)
|
||||||
|
{
|
||||||
|
return LittleShort((unsigned short)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swapping 32bit.
|
||||||
|
inline unsigned int LittleLong (unsigned int x)
|
||||||
|
{
|
||||||
|
return (unsigned int)(
|
||||||
|
(x>>24)
|
||||||
|
| ((x>>8) & 0xff00)
|
||||||
|
| ((x<<8) & 0xff0000)
|
||||||
|
| (x<<24));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int LittleLong (int x)
|
||||||
|
{
|
||||||
|
return (int)(
|
||||||
|
(((unsigned int)x)>>24)
|
||||||
|
| ((((unsigned int)x)>>8) & 0xff00)
|
||||||
|
| ((((unsigned int)x)<<8) & 0xff0000)
|
||||||
|
| (((unsigned int)x)<<24));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline short BigShort(short x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short BigShort(unsigned short x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int BigLong(unsigned int x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int BigLong(int x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
inline short LittleShort(short x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short LittleShort(unsigned short x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int LittleLong(unsigned int x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int LittleLong(int x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
inline short BigShort(short x)
|
||||||
|
{
|
||||||
|
return (short)_byteswap_ushort((unsigned short)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short BigShort(unsigned short x)
|
||||||
|
{
|
||||||
|
return _byteswap_ushort(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int BigLong(int x)
|
||||||
|
{
|
||||||
|
return (int)_byteswap_ulong((unsigned long)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int BigLong(unsigned int x)
|
||||||
|
{
|
||||||
|
return (unsigned int)_byteswap_ulong((unsigned long)x);
|
||||||
|
}
|
||||||
|
#pragma warning (default: 4035)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
inline short BigShort (short x)
|
||||||
|
{
|
||||||
|
return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned short BigShort (unsigned short x)
|
||||||
|
{
|
||||||
|
return (unsigned short)((x>>8) | (x<<8));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int BigLong (unsigned int x)
|
||||||
|
{
|
||||||
|
return (unsigned int)(
|
||||||
|
(x>>24)
|
||||||
|
| ((x>>8) & 0xff00)
|
||||||
|
| ((x<<8) & 0xff0000)
|
||||||
|
| (x<<24));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int BigLong (int x)
|
||||||
|
{
|
||||||
|
return (int)(
|
||||||
|
(((unsigned int)x)>>24)
|
||||||
|
| ((((unsigned int)x)>>8) & 0xff00)
|
||||||
|
| ((((unsigned int)x)<<8) & 0xff0000)
|
||||||
|
| (((unsigned int)x)<<24));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __BIG_ENDIAN__
|
||||||
|
|
||||||
|
// These may be destructive so they should create errors
|
||||||
|
unsigned long BigLong(unsigned long) = delete;
|
||||||
|
long BigLong(long) = delete;
|
||||||
|
unsigned long LittleLong(unsigned long) = delete;
|
||||||
|
long LittleLong(long) = delete;
|
||||||
|
|
||||||
|
|
||||||
|
// Data accessors, since some data is highly likely to be unaligned.
|
||||||
|
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||||
|
inline int GetShort(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return *(const short *)foo;
|
||||||
|
}
|
||||||
|
inline int GetInt(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return *(const int *)foo;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline int GetShort(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return short(foo[0] | (foo[1] << 8));
|
||||||
|
}
|
||||||
|
inline int GetInt(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
inline int GetBigInt(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
inline int GetNativeInt(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return GetBigInt(foo);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline int GetNativeInt(const unsigned char *foo)
|
||||||
|
{
|
||||||
|
return GetInt(foo);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __M_SWAP_H__
|
|
@ -37,26 +37,22 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "opl_mus_player.h"
|
#include "opl_mus_player.h"
|
||||||
#include "opl.h"
|
#include "opl.h"
|
||||||
#include "w_wad.h"
|
#include "o_swap.h"
|
||||||
#include "templates.h"
|
|
||||||
#include "c_cvars.h"
|
|
||||||
#include "doomtype.h"
|
|
||||||
#include "i_musicinterns.h"
|
|
||||||
|
|
||||||
#define IMF_RATE 700.0
|
#define IMF_RATE 700.0
|
||||||
|
|
||||||
EXTERN_CVAR (Int, opl_numchips)
|
OPLmusicBlock::OPLmusicBlock(int core, int numchips)
|
||||||
|
|
||||||
OPLmusicBlock::OPLmusicBlock(int core)
|
|
||||||
{
|
{
|
||||||
currentCore = core;
|
currentCore = core;
|
||||||
scoredata = NULL;
|
scoredata = NULL;
|
||||||
NextTickIn = 0;
|
NextTickIn = 0;
|
||||||
LastOffset = 0;
|
LastOffset = 0;
|
||||||
NumChips = MIN(*opl_numchips, 2);
|
NumChips = std::min(numchips, 2);
|
||||||
Looping = false;
|
Looping = false;
|
||||||
FullPan = false;
|
FullPan = false;
|
||||||
io = NULL;
|
io = NULL;
|
||||||
|
@ -68,11 +64,11 @@ OPLmusicBlock::~OPLmusicBlock()
|
||||||
delete io;
|
delete io;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPLmusicBlock::ResetChips ()
|
void OPLmusicBlock::ResetChips (int numchips)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(ChipAccess);
|
std::lock_guard<std::mutex> lock(ChipAccess);
|
||||||
io->Reset ();
|
io->Reset ();
|
||||||
NumChips = io->Init(currentCore, MIN(*opl_numchips, 2), FullPan, false);
|
NumChips = io->Init(currentCore, std::min(numchips, 2), FullPan, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPLmusicBlock::Restart()
|
void OPLmusicBlock::Restart()
|
||||||
|
@ -83,22 +79,18 @@ void OPLmusicBlock::Restart()
|
||||||
LastOffset = 0;
|
LastOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OPLmusicFile::OPLmusicFile (FileReader &reader, int core)
|
OPLmusicFile::OPLmusicFile (const void *data, size_t length, int core, int numchips, const char *&errormessage)
|
||||||
: OPLmusicBlock(core), ScoreLen ((int)reader.GetLength())
|
: OPLmusicBlock(core, numchips), ScoreLen ((int)length)
|
||||||
{
|
{
|
||||||
if (io == NULL)
|
static char errorbuffer[80];
|
||||||
|
errormessage = nullptr;
|
||||||
|
if (io == nullptr)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoredata = new uint8_t[ScoreLen];
|
scoredata = new uint8_t[ScoreLen];
|
||||||
|
memcpy(scoredata, data, length);
|
||||||
if (reader.Read(scoredata, ScoreLen) != ScoreLen)
|
|
||||||
{
|
|
||||||
fail: delete[] scoredata;
|
|
||||||
scoredata = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 == (NumChips = io->Init(core, NumChips, false, false)))
|
if (0 == (NumChips = io->Init(core, NumChips, false, false)))
|
||||||
{
|
{
|
||||||
|
@ -106,8 +98,7 @@ fail: delete[] scoredata;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for RDosPlay raw OPL format
|
// Check for RDosPlay raw OPL format
|
||||||
if (((uint32_t *)scoredata)[0] == MAKE_ID('R','A','W','A') &&
|
if (!memcmp(scoredata, "RAWADATA", 8))
|
||||||
((uint32_t *)scoredata)[1] == MAKE_ID('D','A','T','A'))
|
|
||||||
{
|
{
|
||||||
RawPlayer = RDosPlay;
|
RawPlayer = RDosPlay;
|
||||||
if (*(uint16_t *)(scoredata + 8) == 0)
|
if (*(uint16_t *)(scoredata + 8) == 0)
|
||||||
|
@ -117,26 +108,27 @@ fail: delete[] scoredata;
|
||||||
SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
|
SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL;
|
||||||
}
|
}
|
||||||
// Check for DosBox OPL dump
|
// Check for DosBox OPL dump
|
||||||
else if (((uint32_t *)scoredata)[0] == MAKE_ID('D','B','R','A') &&
|
else if (!memcmp(scoredata, "DBRAWOPL", 8))
|
||||||
((uint32_t *)scoredata)[1] == MAKE_ID('W','O','P','L'))
|
|
||||||
{
|
{
|
||||||
if (LittleShort(((uint16_t *)scoredata)[5]) == 1)
|
if (LittleShort(((uint16_t *)scoredata)[5]) == 1)
|
||||||
{
|
{
|
||||||
RawPlayer = DosBox1;
|
RawPlayer = DosBox1;
|
||||||
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
||||||
ScoreLen = MIN<int>(ScoreLen - 24, LittleLong(((uint32_t *)scoredata)[4])) + 24;
|
ScoreLen = std::min<int>(ScoreLen - 24, LittleLong(((uint32_t *)scoredata)[4])) + 24;
|
||||||
}
|
}
|
||||||
else if (((uint32_t *)scoredata)[2] == MAKE_ID(2,0,0,0))
|
else if (LittleLong(((uint32_t *)scoredata)[2]) == 2)
|
||||||
{
|
{
|
||||||
bool okay = true;
|
bool okay = true;
|
||||||
if (scoredata[21] != 0)
|
if (scoredata[21] != 0)
|
||||||
{
|
{
|
||||||
Printf("Unsupported DOSBox Raw OPL format %d\n", scoredata[20]);
|
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL format %d\n", scoredata[20]);
|
||||||
|
errormessage = errorbuffer;
|
||||||
okay = false;
|
okay = false;
|
||||||
}
|
}
|
||||||
if (scoredata[22] != 0)
|
if (scoredata[22] != 0)
|
||||||
{
|
{
|
||||||
Printf("Unsupported DOSBox Raw OPL compression %d\n", scoredata[21]);
|
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL compression %d\n", scoredata[21]);
|
||||||
|
errormessage = errorbuffer;
|
||||||
okay = false;
|
okay = false;
|
||||||
}
|
}
|
||||||
if (!okay)
|
if (!okay)
|
||||||
|
@ -144,17 +136,17 @@ fail: delete[] scoredata;
|
||||||
RawPlayer = DosBox2;
|
RawPlayer = DosBox2;
|
||||||
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
SamplesPerTick = OPL_SAMPLE_RATE / 1000;
|
||||||
int headersize = 0x1A + scoredata[0x19];
|
int headersize = 0x1A + scoredata[0x19];
|
||||||
ScoreLen = MIN<int>(ScoreLen - headersize, LittleLong(((uint32_t *)scoredata)[3]) * 2) + headersize;
|
ScoreLen = std::min<int>(ScoreLen - headersize, LittleLong(((uint32_t *)scoredata)[3]) * 2) + headersize;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Printf("Unsupported DOSBox Raw OPL version %d.%d\n", LittleShort(((uint16_t *)scoredata)[4]), LittleShort(((uint16_t *)scoredata)[5]));
|
snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL version %d.%d\n", LittleShort(((uint16_t *)scoredata)[4]), LittleShort(((uint16_t *)scoredata)[5]));
|
||||||
|
errormessage = errorbuffer;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for modified IMF format (includes a header)
|
// Check for modified IMF format (includes a header)
|
||||||
else if (((uint32_t *)scoredata)[0] == MAKE_ID('A','D','L','I') &&
|
else if (!memcmp(scoredata, "ADLIB\1", 6))
|
||||||
scoredata[4] == 'B' && scoredata[5] == 1)
|
|
||||||
{
|
{
|
||||||
int songlen;
|
int songlen;
|
||||||
uint8_t *max = scoredata + ScoreLen;
|
uint8_t *max = scoredata + ScoreLen;
|
||||||
|
@ -170,9 +162,7 @@ fail: delete[] scoredata;
|
||||||
if (score < max) score++; // Skip unknown byte
|
if (score < max) score++; // Skip unknown byte
|
||||||
if (score + 8 > max)
|
if (score + 8 > max)
|
||||||
{ // Not enough room left for song data
|
{ // Not enough room left for song data
|
||||||
delete[] scoredata;
|
goto fail;
|
||||||
scoredata = NULL;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
songlen = LittleLong(*(uint32_t *)score);
|
songlen = LittleLong(*(uint32_t *)score);
|
||||||
if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata))
|
if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata))
|
||||||
|
@ -182,10 +172,18 @@ fail: delete[] scoredata;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
errormessage = "Unknown OPL format";
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
Restart ();
|
Restart ();
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
delete[] scoredata;
|
||||||
|
scoredata = nullptr;
|
||||||
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OPLmusicFile::~OPLmusicFile ()
|
OPLmusicFile::~OPLmusicFile ()
|
||||||
|
@ -262,7 +260,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
|
||||||
{
|
{
|
||||||
double ticky = NextTickIn;
|
double ticky = NextTickIn;
|
||||||
int tick_in = int(NextTickIn);
|
int tick_in = int(NextTickIn);
|
||||||
int samplesleft = MIN(numsamples, tick_in);
|
int samplesleft = std::min(numsamples, tick_in);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (samplesleft > 0)
|
if (samplesleft > 0)
|
||||||
|
@ -367,7 +365,7 @@ void OPLmusicBlock::OffsetSamples(float *buff, int count)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ramp = MIN(count, MAX(196, largest_at));
|
ramp = std::min(count, std::max(196, largest_at));
|
||||||
step = (offset - LastOffset) / ramp;
|
step = (offset - LastOffset) / ramp;
|
||||||
}
|
}
|
||||||
offset = LastOffset;
|
offset = LastOffset;
|
||||||
|
@ -531,34 +529,3 @@ int OPLmusicFile::PlayTick ()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OPLmusicFile::OPLmusicFile(int core, const OPLmusicFile *source, const char *filename)
|
|
||||||
: OPLmusicBlock(core)
|
|
||||||
{
|
|
||||||
ScoreLen = source->ScoreLen;
|
|
||||||
scoredata = new uint8_t[ScoreLen];
|
|
||||||
memcpy(scoredata, source->scoredata, ScoreLen);
|
|
||||||
SamplesPerTick = source->SamplesPerTick;
|
|
||||||
RawPlayer = source->RawPlayer;
|
|
||||||
score = source->score;
|
|
||||||
NumChips = source->NumChips;
|
|
||||||
WhichChip = 0;
|
|
||||||
if (io != NULL)
|
|
||||||
{
|
|
||||||
delete io;
|
|
||||||
}
|
|
||||||
io = new DiskWriterIO(filename);
|
|
||||||
NumChips = io->Init(core, NumChips, false, false);
|
|
||||||
Restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OPLmusicFile::Dump()
|
|
||||||
{
|
|
||||||
int time;
|
|
||||||
|
|
||||||
time = PlayTick();
|
|
||||||
while (time != 0)
|
|
||||||
{
|
|
||||||
io->WriteDelay(time);
|
|
||||||
time = PlayTick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
#include "musicblock.h"
|
#include "musicblock.h"
|
||||||
|
|
||||||
class FileReader;
|
|
||||||
|
|
||||||
class OPLmusicBlock : public musicBlock
|
class OPLmusicBlock : public musicBlock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OPLmusicBlock(int core);
|
OPLmusicBlock(int core, int numchips);
|
||||||
virtual ~OPLmusicBlock();
|
virtual ~OPLmusicBlock();
|
||||||
|
|
||||||
bool ServiceStream(void *buff, int numbytes);
|
bool ServiceStream(void *buff, int numbytes);
|
||||||
void ResetChips();
|
void ResetChips(int numchips);
|
||||||
|
|
||||||
virtual void Restart();
|
virtual void Restart();
|
||||||
|
|
||||||
|
@ -36,17 +36,15 @@ protected:
|
||||||
class OPLmusicFile : public OPLmusicBlock
|
class OPLmusicFile : public OPLmusicBlock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OPLmusicFile(FileReader &reader, int core);
|
OPLmusicFile(const void *data, size_t length, int core, int numchips, const char *&errormessage);
|
||||||
OPLmusicFile(int core, const OPLmusicFile *source, const char *filename);
|
|
||||||
virtual ~OPLmusicFile();
|
virtual ~OPLmusicFile();
|
||||||
|
|
||||||
bool IsValid() const;
|
bool IsValid() const;
|
||||||
void SetLooping(bool loop);
|
void SetLooping(bool loop);
|
||||||
void Restart();
|
void Restart();
|
||||||
void Dump();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
OPLmusicFile(int core) : OPLmusicBlock(core) {}
|
OPLmusicFile(int core, int numchips) : OPLmusicBlock(core, numchips) {}
|
||||||
int PlayTick();
|
int PlayTick();
|
||||||
|
|
||||||
enum { RDosPlay, IMF, DosBox1, DosBox2 } RawPlayer;
|
enum { RDosPlay, IMF, DosBox1, DosBox2 } RawPlayer;
|
||||||
|
|
Loading…
Reference in a new issue