Update libOPNMIDI library to 1.5.0

## 1.5.0   2020-09-28
 * Drum note length expanding is now supported in real-time mode (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added support for OPNA chip with Neko Project II Kai YM2602 emulator usage (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added VGM file dumper which allows to output OPN2 commands into VGM file. (A new MIDI to VGM tool is now created with basing on libOPNMIDI)
 * Fixed an incorrect work of CC-121 (See https://github.com/Wohlstand/libADLMIDI/issues/227 for details)
 * Internality has been refactored and improved
This commit is contained in:
Wohlstand 2020-09-28 21:23:55 +03:00 committed by Christoph Oelckers
parent 1d4a016c41
commit 72c23d98a3
81 changed files with 25699 additions and 2083 deletions

View file

@ -10,16 +10,34 @@ add_definitions(-DOPNMIDI_DISABLE_MIDI_SEQUENCER)
add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR)
add_library( opn STATIC
chips/gens_opn2.cpp
chips/gens/Ym2612_Emu.cpp
chips/mame/mame_ym2612fm.c
chips/mame_opn2.cpp
chips/nuked_opn2.cpp
chips/nuked/ym3438.c
opnmidi.cpp
opnmidi_load.cpp
opnmidi_private.cpp
opnmidi.cpp
opnmidi_midiplay.cpp
opnmidi_opn2.cpp
opnmidi_private.cpp
wopn/wopn_file.c )
chips/np2/fmgen_fmgen.cpp
chips/np2/fmgen_opna.cpp
chips/np2/fmgen_fmtimer.cpp
chips/np2/fmgen_file.cpp
chips/np2/fmgen_psg.cpp
chips/mame_opn2.cpp
chips/gens_opn2.cpp
chips/mame_opna.cpp
chips/np2_opna.cpp
chips/mamefm/ymdeltat.cpp
chips/mamefm/resampler.cpp
chips/mamefm/fm.cpp
chips/nuked_opn2.cpp
chips/gens/Ym2612_Emu.cpp
chips/gx_opn2.cpp
chips/pmdwin_opna.cpp
chips/nuked/ym3438.c
chips/gx/gx_ym2612.c
chips/pmdwin/opna.c
chips/pmdwin/psg.c
chips/pmdwin/rhythmdata.c
chips/mamefm/emu2149.c
chips/mame/mame_ym2612fm.c
wopn/wopn_file.c
)
target_link_libraries( opn )

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -23,8 +23,9 @@
#include "gens/Ym2612_Emu.h"
GensOPN2::GensOPN2()
: chip(new Ym2612_Emu())
GensOPN2::GensOPN2(OPNFamily f)
: OPNChipBaseBufferedT(f),
chip(new Ym2612_Emu())
{
setRate(m_rate, m_clock);
}
@ -37,7 +38,7 @@ GensOPN2::~GensOPN2()
void GensOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseBufferedT::setRate(rate, clock);
uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast<uint32_t>(nativeRate);
uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate();
chip->set_rate(chipRate, clock); // implies reset()
}

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -28,7 +28,7 @@ class GensOPN2 final : public OPNChipBaseBufferedT<GensOPN2>
{
Ym2612_Emu *chip;
public:
GensOPN2();
explicit GensOPN2(OPNFamily f);
~GensOPN2() override;
bool canRunAtPcmRate() const override { return true; }

2271
thirdparty/opnmidi/chips/gx/gx_ym2612.c vendored Normal file

File diff suppressed because it is too large Load diff

53
thirdparty/opnmidi/chips/gx/gx_ym2612.h vendored Normal file
View file

@ -0,0 +1,53 @@
/*
**
** software implementation of Yamaha FM sound generator (YM2612/YM3438)
**
** Original code (MAME fm.c)
**
** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net)
** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development
**
** Version 1.4 (final beta)
**
** Additional code & fixes by Eke-Eke for Genesis Plus GX
** Adaptations by Jean Pierre Cimalando for use in libOPNMIDI.
** (based on fee2bc8 dated Jan 7th, 2018)
**
*/
#ifndef _H_YM2612_
#define _H_YM2612_
#if defined(__cplusplus)
extern "C" {
#endif
enum {
YM2612_DISCRETE = 0,
YM2612_INTEGRATED,
YM2612_ENHANCED
};
struct YM2612GX;
typedef struct YM2612GX YM2612GX;
/* typedef signed int FMSAMPLE; */
typedef signed short FMSAMPLE;
extern YM2612GX *YM2612GXAlloc();
extern void YM2612GXFree(YM2612GX *ym2612);
extern void YM2612GXInit(YM2612GX *ym2612);
extern void YM2612GXConfig(YM2612GX *ym2612, int type);
extern void YM2612GXResetChip(YM2612GX *ym2612);
extern void YM2612GXPreGenerate(YM2612GX *ym2612);
extern void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count);
extern void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame);
extern void YM2612GXWrite(YM2612GX *ym2612, unsigned int a, unsigned int v);
extern void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v);
extern unsigned int YM2612GXRead(YM2612GX *ym2612);
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif /* _YM2612_ */

84
thirdparty/opnmidi/chips/gx_opn2.cpp vendored Normal file
View file

@ -0,0 +1,84 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gx_opn2.h"
#include <cstring>
#include "gx/gx_ym2612.h"
GXOPN2::GXOPN2(OPNFamily f)
: OPNChipBaseT(f),
m_chip(YM2612GXAlloc()),
m_framecount(0)
{
YM2612GXInit(m_chip);
YM2612GXConfig(m_chip, YM2612_DISCRETE);
setRate(m_rate, m_clock);
}
GXOPN2::~GXOPN2()
{
YM2612GXFree(m_chip);
}
void GXOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseT::setRate(rate, clock);
YM2612GXResetChip(m_chip);
}
void GXOPN2::reset()
{
OPNChipBaseT::reset();
YM2612GXResetChip(m_chip);
}
void GXOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
YM2612GXWrite(m_chip, 0 + port * 2, addr);
YM2612GXWrite(m_chip, 1 + port * 2, data);
}
void GXOPN2::writePan(uint16_t chan, uint8_t data)
{
YM2612GXWritePan(m_chip, chan, data);
}
void GXOPN2::nativePreGenerate()
{
YM2612GXPreGenerate(m_chip);
m_framecount = 0;
}
void GXOPN2::nativePostGenerate()
{
YM2612GXPostGenerate(m_chip, m_framecount);
}
void GXOPN2::nativeGenerate(int16_t *frame)
{
YM2612GXGenerateOneNative(m_chip, frame);
++m_framecount;
}
const char *GXOPN2::emulatorName()
{
return "Genesis Plus GX";
}

46
thirdparty/opnmidi/chips/gx_opn2.h vendored Normal file
View file

@ -0,0 +1,46 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GX_OPN2_H
#define GX_OPN2_H
#include "opn_chip_base.h"
struct YM2612GX;
class GXOPN2 final : public OPNChipBaseT<GXOPN2>
{
YM2612GX *m_chip;
unsigned int m_framecount;
public:
explicit GXOPN2(OPNFamily f);
~GXOPN2() override;
bool canRunAtPcmRate() const override { return false; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void writePan(uint16_t chan, uint8_t data) override;
void nativePreGenerate() override;
void nativePostGenerate() override;
void nativeGenerate(int16_t *frame) override;
const char *emulatorName() override;
};
#endif // GX_OPN2_H

View file

@ -133,6 +133,9 @@
/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
/************************************************************************/
#define RSM_ENABLE 0
#define RSM_FRAC 10
#include <stdlib.h>
#include <string.h> /* for memset */
#include <stddef.h> /* for NULL */
@ -147,9 +150,6 @@ static stream_sample_t *DUMMYBUF = NULL;
#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438)
#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
#define RSM_ENABLE 0
#define RSM_FRAC 10
/* globals */
#define TYPE_SSG 0x01 /* SSG support */
#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
@ -2043,7 +2043,7 @@ static void OPNWriteReg(FM_OPN *OPN, int r, int v)
case 1: /* 0xa4-0xa6 : FNUM2,BLK */
OPN->ST.fn_h = v&0x3f;
#ifdef USE_VGM_INIT_SWITCH
if (IsVGMInit) // workaround for stupid Kega Fusion init block
if (IsVGMInit) /* workaround for stupid Kega Fusion init block */
CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF);
#endif
break;

View file

@ -17,14 +17,19 @@ typedef signed int INT32;
#endif
/* 64-bit values */
#ifndef _WINDOWS_H
#ifdef _MSC_VER
#if !defined(_WINDOWS_H) && defined(RSM_ENABLE)
# ifdef _MSC_VER
typedef signed __int64 INT64;
typedef unsigned __int64 UINT64;
#else
__extension__ typedef unsigned long long UINT64;
__extension__ typedef signed long long INT64;
#endif
# else
# if (__WORDSIZE == 64) || defined(_WIN64)
typedef signed long int INT64;
typedef unsigned long int UINT64;
# else
__extension__ typedef signed long long int INT64;
__extension__ typedef unsigned long long int UINT64;
# endif
# endif
#endif
/* offsets and addresses are 32-bit (for now...) */
@ -42,7 +47,7 @@ typedef INT16 stream_sample_t;
#endif
#if defined(_MSC_VER)
//#define INLINE static __forceinline
/* #define INLINE static __forceinline */
#define INLINE static __inline
#elif defined(__GNUC__)
#define INLINE static __inline__

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -23,7 +23,8 @@
#include <cstdlib>
#include <assert.h>
MameOPN2::MameOPN2()
MameOPN2::MameOPN2(OPNFamily f)
: OPNChipBaseT(f)
{
chip = NULL;
setRate(m_rate, m_clock);
@ -39,7 +40,7 @@ void MameOPN2::setRate(uint32_t rate, uint32_t clock)
OPNChipBaseT::setRate(rate, clock);
if(chip)
ym2612_shutdown(chip);
uint32_t chipRate = isRunningAtPcmRate() ? rate : static_cast<uint32_t>(nativeRate);
uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate();
chip = ym2612_init(NULL, (int)clock, (int)chipRate, NULL, NULL);
ym2612_reset_chip(chip);
}

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,7 +27,7 @@ class MameOPN2 final : public OPNChipBaseT<MameOPN2>
{
void *chip;
public:
MameOPN2();
explicit MameOPN2(OPNFamily f);
~MameOPN2() override;
bool canRunAtPcmRate() const override { return true; }

202
thirdparty/opnmidi/chips/mame_opna.cpp vendored Normal file
View file

@ -0,0 +1,202 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mame_opna.h"
#include "mamefm/fm.h"
#include "mamefm/fmopn_2608rom.h"
#include "mamefm/2608intf.h"
#include "mamefm/resampler.hpp"
struct MameOPNA::Impl {
ym2608_device dev;
void *chip;
// typedef chip::SincResampler Resampler;
typedef chip::LinearResampler Resampler;
Resampler *psgrsm;
int32_t *psgbuffer;
static const ssg_callbacks cbssg;
// callbacks
static uint8_t cbInternalReadByte(device_t *, offs_t);
static uint8_t cbExternalReadByte(device_t *, offs_t) { return 0; }
static void cbExternalWriteByte(device_t *, offs_t, uint8_t) {}
static void cbHandleTimer(device_t *, int, int, int) {}
static void cbHandleIRQ(device_t *, int) {}
static void cbSsgSetClock(device_t *dev, int clock);
static void cbSsgWrite(device_t *dev, int addr, int data);
static int cbSsgRead(device_t *dev);
static void cbSsgReset(device_t *dev);
};
const ssg_callbacks MameOPNA::Impl::cbssg =
{
&cbSsgSetClock,
&cbSsgWrite,
&cbSsgRead,
&cbSsgReset,
};
MameOPNA::MameOPNA(OPNFamily f)
: OPNChipBaseBufferedT(f), impl(new Impl)
{
impl->chip = NULL;
impl->psgrsm = NULL;
impl->psgbuffer = NULL;
setRate(m_rate, m_clock);
}
MameOPNA::~MameOPNA()
{
delete impl->psgrsm;
delete[] impl->psgbuffer;
ym2608_shutdown(impl->chip);
delete impl;
}
void MameOPNA::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseBufferedT::setRate(rate, clock);
if(impl->chip)
ym2608_shutdown(impl->chip);
uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate();
ym2608_device *device = &impl->dev;
void *chip = impl->chip = ym2608_init(
device, (int)clock, (int)chipRate,
&Impl::cbInternalReadByte, &Impl::cbExternalReadByte,
&Impl::cbExternalWriteByte,
&Impl::cbHandleTimer, &Impl::cbHandleIRQ, &Impl::cbssg);
PSG *psg = &device->m_psg;
memset(psg, 0, sizeof(PSG));
uint32_t psgRate = clock / 32;
PSG_init(psg, clock / 4, psgRate); // TODO libOPNMIDI verify clocks
PSG_setVolumeMode(psg, 1); // YM2149 volume mode
delete impl->psgrsm;
Impl::Resampler *psgrsm = impl->psgrsm = new Impl::Resampler;
psgrsm->init(psgRate, chipRate, 40);
delete[] impl->psgbuffer;
impl->psgbuffer = new int32_t[2 * psgrsm->calculateInternalSampleSize(buffer_size)];
ym2608_reset_chip(chip);
ym2608_write(chip, 0, 0x29);
ym2608_write(chip, 1, 0x9f);
}
void MameOPNA::reset()
{
OPNChipBaseBufferedT::reset();
void *chip = impl->chip;
ym2608_reset_chip(chip);
ym2608_write(chip, 0, 0x29);
ym2608_write(chip, 1, 0x9f);
}
void MameOPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
void *chip = impl->chip;
ym2608_write(chip, 0 + (int)(port) * 2, (uint8_t)addr);
ym2608_write(chip, 1 + (int)(port) * 2, data);
}
void MameOPNA::writePan(uint16_t chan, uint8_t data)
{
void *chip = impl->chip;
ym2608_write_pan(chip, (int)chan, data);
}
void MameOPNA::nativeGenerateN(int16_t *output, size_t frames)
{
void *chip = impl->chip;
FMSAMPLE fmLR[2 * buffer_size];
FMSAMPLE *fmR = fmLR + buffer_size;
FMSAMPLE *fmbufs[2] = { fmLR, fmR };
ym2608_update_one(chip, fmbufs, (int)frames);
PSG *psg = &impl->dev.m_psg;
Impl::Resampler *psgrsm = impl->psgrsm;
size_t psgframes = psgrsm->calculateInternalSampleSize(frames);
int32_t *rawpsgLR = impl->psgbuffer;
int32_t *rawpsgR = rawpsgLR + psgframes;
int32_t *rawpsgbufs[2] = { rawpsgLR, rawpsgR };
PSG_calc_stereo(psg, rawpsgbufs, (int32_t)psgframes);
int32_t **psgbufs = psgrsm->interpolate(rawpsgbufs, frames, psgframes);
int32_t *psgL = psgbufs[0];
int32_t *psgR = psgbufs[1];
for(size_t i = 0; i < frames; ++i)
{
int32_t l = fmLR[i] + psgL[i];
l = (l > -32768) ? l : -32768;
l = (l < 32767) ? l : 32767;
int32_t r = fmR[i] + psgR[i];
r = (r > -32768) ? r : -32768;
r = (r < 32767) ? r : 32767;
output[2 * i] = l;
output[2 * i + 1] = r;
}
}
const char *MameOPNA::emulatorName()
{
return "MAME YM2608"; // git 2018-12-15 rev 8ab05c0
}
uint8_t MameOPNA::Impl::cbInternalReadByte(device_t *dev, offs_t off)
{
(void)dev;
return YM2608_ADPCM_ROM[off & 0x1fff];
}
void MameOPNA::Impl::cbSsgSetClock(device_t *dev, int clock)
{
ym2608_device *ym = static_cast<ym2608_device *>(dev);
PSG_set_clock(&ym->m_psg, (uint32_t)clock);
}
void MameOPNA::Impl::cbSsgWrite(device_t *dev, int addr, int data)
{
ym2608_device *ym = static_cast<ym2608_device *>(dev);
PSG_writeIO(&ym->m_psg, (uint32_t)addr, (uint32_t)data);
}
int MameOPNA::Impl::cbSsgRead(device_t *dev)
{
ym2608_device *ym = static_cast<ym2608_device *>(dev);
return PSG_readIO(&ym->m_psg);
}
void MameOPNA::Impl::cbSsgReset(device_t *dev)
{
ym2608_device *ym = static_cast<ym2608_device *>(dev);
return PSG_reset(&ym->m_psg);
}

45
thirdparty/opnmidi/chips/mame_opna.h vendored Normal file
View file

@ -0,0 +1,45 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MAME_OPNA_H
#define MAME_OPNA_H
#include "opn_chip_base.h"
class MameOPNA final : public OPNChipBaseBufferedT<MameOPNA>
{
struct Impl;
Impl *impl;
public:
explicit MameOPNA(OPNFamily f);
~MameOPNA() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void writePan(uint16_t chan, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerateN(int16_t *output, size_t frames) override;
const char *emulatorName() override;
};
#endif

View file

@ -0,0 +1,48 @@
/*
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPNMIDI_MAMEFM_2608INTF_H
#define OPNMIDI_MAMEFM_2608INTF_H
#include "emu.h"
#include "emu2149.h"
struct ym2608_device : device_t
{
void update_request();
static void update_request(device_t *dev);
PSG m_psg;
};
inline void ym2608_device::update_request()
{
// libOPNMIDI: use this callback maybe
}
inline void ym2608_device::update_request(device_t *dev)
{
static_cast<ym2608_device *>(dev)->update_request();
}
#endif

104
thirdparty/opnmidi/chips/mamefm/emu.h vendored Normal file
View file

@ -0,0 +1,104 @@
/*
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPNMIDI_MAMEFM_EMU_H
#define OPNMIDI_MAMEFM_EMU_H
#include <new>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <assert.h>
#if !defined(M_PI)
# define M_PI 3.14159265358979323846
#endif
#if __cplusplus <= 199711L
#define CONSTEXPR
#else
#define CONSTEXPR constexpr
#endif
#if __cplusplus <= 199711L
#define NULLPTR NULL
#else
#define NULLPTR nullptr
#endif
typedef uint32_t offs_t;
inline void vlogerror(const char *fmt, va_list ap)
{
#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE)
vfprintf(stderr, fmt, ap);
#else
(void)fmt;
(void)ap;
#endif
}
inline void logerror(const char *fmt, ...)
{
#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE)
va_list ap;
va_start(ap, fmt);
vlogerror(fmt, ap);
va_end(ap);
#else
(void)fmt;
#endif
}
struct device_t
{
void logerror(const char *fmt, ...);
void vlogerror(const char *fmt, va_list ap);
};
inline void device_t::logerror(const char *fmt, ...)
{
#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE)
va_list ap;
va_start(ap, fmt);
vlogerror(fmt, ap);
va_end(ap);
#else
(void)fmt;
#endif
}
inline void device_t::vlogerror(const char *fmt, va_list ap)
{
#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE)
::vlogerror(fmt, ap);
#else
(void)fmt;
(void)ap;
#endif
}
#endif

View file

@ -0,0 +1,529 @@
/* SPDX-License-Identifier: MIT */
/****************************************************************************
emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001
2001 04-28 : Version 1.00beta -- 1st Beta Release.
2001 08-14 : Version 1.10
2001 10-03 : Version 1.11 -- Added PSG_set_quality().
2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close.
2002 10-13 : Version 1.14 -- Fixed the envelope unit.
2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask
2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope
frequency register is written before key-on.
References:
psg.vhd -- 2000 written by Kazuhiro Tsujikawa.
s_fme7.c -- 1999,2000 written by Mamiya (NEZplug).
ay8910.c -- 1998-2001 Author unknown (MAME).
MSX-Datapack -- 1991 ASCII Corp.
AY-3-8910 data sheet
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emu2149.h"
static UINT32 voltbl[2][32] = {
{0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09,
0x0B, 0x0D, 0x0F, 0x12,
0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97,
0xB4, 0xD6, 0xEB, 0xFF},
{0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07,
0x0B, 0x0B, 0x0F, 0x0F,
0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F,
0xB4, 0xB4, 0xFF, 0xFF}
};
#define GETA_BITS 24
static void
internal_refresh (PSG * psg)
{
if (psg->quality)
{
psg->base_incr = 1 << GETA_BITS;
psg->realstep = (UINT32) ((1 << 31) / psg->rate);
psg->psgstep = (UINT32) ((1 << 31) / (psg->clk / 8));
psg->psgtime = 0;
}
else
{
psg->base_incr =
(UINT32) ((double) psg->clk * (1 << GETA_BITS) / (8.0 * psg->rate));
}
}
EMU2149_API void
PSG_set_clock(PSG * psg, UINT32 c)
{
psg->clk = c;
internal_refresh(psg);
}
EMU2149_API void
PSG_set_rate (PSG * psg, UINT32 r)
{
psg->rate = r ? r : 44100;
internal_refresh (psg);
}
EMU2149_API void
PSG_set_quality (PSG * psg, UINT32 q)
{
psg->quality = q;
internal_refresh (psg);
}
EMU2149_API void
PSG_init (PSG * psg, UINT32 c, UINT32 r)
{
memset(psg, 0x00, sizeof(PSG));
PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT);
psg->clk = c;
psg->rate = r ? r : 44100;
PSG_set_quality (psg, 0);
psg->stereo_mask[0] = 0x03;
psg->stereo_mask[1] = 0x03;
psg->stereo_mask[2] = 0x03;
}
EMU2149_API void
PSG_setFlags (PSG * psg, UINT8 flags)
{
if (flags & EMU2149_ZX_STEREO)
{
/* ABC Stereo */
psg->stereo_mask[0] = 0x01;
psg->stereo_mask[1] = 0x03;
psg->stereo_mask[2] = 0x02;
}
else
{
psg->stereo_mask[0] = 0x03;
psg->stereo_mask[1] = 0x03;
psg->stereo_mask[2] = 0x03;
}
return;
}
EMU2149_API void
PSG_setVolumeMode (PSG * psg, int type)
{
switch (type)
{
case 1:
psg->voltbl = voltbl[EMU2149_VOL_YM2149];
break;
case 2:
psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910];
break;
default:
psg->voltbl = voltbl[EMU2149_VOL_DEFAULT];
break;
}
}
EMU2149_API UINT32
PSG_setMask (PSG *psg, UINT32 mask)
{
UINT32 ret = 0;
if(psg)
{
ret = psg->mask;
psg->mask = mask;
}
return ret;
}
EMU2149_API void
PSG_setStereoMask (PSG *psg, UINT32 mask)
{
if(psg)
{
psg->stereo_mask[0] = (mask >>0) &3;
psg->stereo_mask[1] = (mask >>2) &3;
psg->stereo_mask[2] = (mask >>4) &3;
}
}
EMU2149_API UINT32
PSG_toggleMask (PSG *psg, UINT32 mask)
{
UINT32 ret = 0;
if(psg)
{
ret = psg->mask;
psg->mask ^= mask;
}
return ret;
}
EMU2149_API void
PSG_reset (PSG * psg)
{
int i;
psg->base_count = 0;
for (i = 0; i < 3; i++)
{
psg->cout[i] = 0;
psg->count[i] = 0x1000;
psg->freq[i] = 0;
psg->edge[i] = 0;
psg->volume[i] = 0;
}
psg->mask = 0;
for (i = 0; i < 16; i++)
psg->reg[i] = 0;
psg->adr = 0;
psg->noise_seed = 0xffff;
psg->noise_count = 0x40;
psg->noise_freq = 0;
psg->env_volume = 0;
psg->env_ptr = 0;
psg->env_freq = 0;
psg->env_count = 0;
psg->env_pause = 1;
psg->out = 0;
}
EMU2149_API void
PSG_delete (PSG * psg)
{
free (psg);
}
EMU2149_API UINT8
PSG_readIO (PSG * psg)
{
return (UINT8) (psg->reg[psg->adr]);
}
EMU2149_API UINT8
PSG_readReg (PSG * psg, UINT32 reg)
{
return (UINT8) (psg->reg[reg & 0x1f]);
}
EMU2149_API void
PSG_writeIO (PSG * psg, UINT32 adr, UINT32 val)
{
if (adr & 1)
PSG_writeReg (psg, psg->adr, val);
else
psg->adr = val & 0x1f;
}
/*INLINE*/ static INT16
calc (PSG * psg)
{
int i, noise;
UINT32 incr;
INT32 mix = 0;
psg->base_count += psg->base_incr;
incr = (psg->base_count >> GETA_BITS);
psg->base_count &= (1 << GETA_BITS) - 1;
/* Envelope */
psg->env_count += incr;
while (psg->env_count>=0x10000 && psg->env_freq!=0)
{
if (!psg->env_pause)
{
if(psg->env_face)
psg->env_ptr = (psg->env_ptr + 1) & 0x3f ;
else
psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f;
}
if (psg->env_ptr & 0x20) /* if carry or borrow */
{
if (psg->env_continue)
{
if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1;
if (psg->env_hold) psg->env_pause = 1;
psg->env_ptr = psg->env_face?0:0x1f;
}
else
{
psg->env_pause = 1;
psg->env_ptr = 0;
}
}
psg->env_count -= psg->env_freq;
}
/* Noise */
psg->noise_count += incr;
if (psg->noise_count & 0x40)
{
if (psg->noise_seed & 1)
psg->noise_seed ^= 0x24000;
psg->noise_seed >>= 1;
psg->noise_count -= psg->noise_freq;
}
noise = psg->noise_seed & 1;
/* Tone */
for (i = 0; i < 3; i++)
{
psg->count[i] += incr;
if (psg->count[i] & 0x1000)
{
if (psg->freq[i] > 1)
{
psg->edge[i] = !psg->edge[i];
psg->count[i] -= psg->freq[i];
}
else
{
psg->edge[i] = 1;
}
}
psg->cout[i] = 0; /* BS maintaining cout for stereo mix */
if (psg->mask&PSG_MASK_CH(i))
continue;
if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise))
{
if (!(psg->volume[i] & 32))
psg->cout[i] = psg->voltbl[psg->volume[i] & 31];
else
psg->cout[i] = psg->voltbl[psg->env_ptr];
mix += psg->cout[i];
}
}
return (INT16) mix;
}
EMU2149_API INT16
PSG_calc (PSG * psg)
{
if (!psg->quality)
return (INT16) (calc (psg) << 4);
/* Simple rate converter */
while (psg->realstep > psg->psgtime)
{
psg->psgtime += psg->psgstep;
psg->out += calc (psg);
psg->out >>= 1;
}
psg->psgtime = psg->psgtime - psg->realstep;
return (INT16) (psg->out << 4);
}
/*INLINE*/ static void
calc_stereo (PSG * psg, INT32 out[2])
{
int i, noise;
UINT32 incr;
INT32 l = 0, r = 0;
psg->base_count += psg->base_incr;
incr = (psg->base_count >> GETA_BITS);
psg->base_count &= (1 << GETA_BITS) - 1;
/* Envelope */
psg->env_count += incr;
while (psg->env_count>=0x10000 && psg->env_freq!=0)
{
if (!psg->env_pause)
{
if(psg->env_face)
psg->env_ptr = (psg->env_ptr + 1) & 0x3f ;
else
psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f;
}
if (psg->env_ptr & 0x20) /* if carry or borrow */
{
if (psg->env_continue)
{
if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1;
if (psg->env_hold) psg->env_pause = 1;
psg->env_ptr = psg->env_face?0:0x1f;
}
else
{
psg->env_pause = 1;
psg->env_ptr = 0;
}
}
psg->env_count -= psg->env_freq;
}
/* Noise */
psg->noise_count += incr;
if (psg->noise_count & 0x40)
{
if (psg->noise_seed & 1)
psg->noise_seed ^= 0x24000;
psg->noise_seed >>= 1;
psg->noise_count -= psg->noise_freq;
}
noise = psg->noise_seed & 1;
/* Tone */
for (i = 0; i < 3; i++)
{
psg->count[i] += incr;
if (psg->count[i] & 0x1000)
{
if (psg->freq[i] > 1)
{
psg->edge[i] = !psg->edge[i];
psg->count[i] -= psg->freq[i];
}
else
{
psg->edge[i] = 1;
}
}
psg->cout[i] = 0; /* BS maintaining cout for stereo mix */
if (psg->mask&PSG_MASK_CH(i))
continue;
if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise))
{
if (!(psg->volume[i] & 32))
psg->cout[i] = psg->voltbl[psg->volume[i] & 31];
else
psg->cout[i] = psg->voltbl[psg->env_ptr];
if (psg->stereo_mask[i] & 0x01)
l += psg->cout[i];
if (psg->stereo_mask[i] & 0x02)
r += psg->cout[i];
}
}
out[0] = l << 5;
out[1] = r << 5;
return;
}
EMU2149_API void
PSG_calc_stereo (PSG * psg, INT32 **out, INT32 samples)
{
INT32 *bufMO = out[0];
INT32 *bufRO = out[1];
INT32 buffers[2];
int i;
for (i = 0; i < samples; i ++)
{
if (!psg->quality)
{
calc_stereo (psg, buffers);
bufMO[i] = buffers[0];
bufRO[i] = buffers[1];
}
else
{
while (psg->realstep > psg->psgtime)
{
psg->psgtime += psg->psgstep;
psg->sprev[0] = psg->snext[0];
psg->sprev[1] = psg->snext[1];
calc_stereo (psg, psg->snext);
}
psg->psgtime -= psg->realstep;
bufMO[i] = (INT32) (((double) psg->snext[0] * (psg->psgstep - psg->psgtime)
+ (double) psg->sprev[0] * psg->psgtime) / psg->psgstep);
bufRO[i] = (INT32) (((double) psg->snext[1] * (psg->psgstep - psg->psgtime)
+ (double) psg->sprev[1] * psg->psgtime) / psg->psgstep);
}
}
}
EMU2149_API void
PSG_writeReg (PSG * psg, UINT32 reg, UINT32 val)
{
int c;
if (reg > 15) return;
psg->reg[reg] = (UINT8) (val & 0xff);
switch (reg)
{
case 0:
case 2:
case 4:
case 1:
case 3:
case 5:
c = reg >> 1;
psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2];
break;
case 6:
psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1);
break;
case 7:
psg->tmask[0] = (val & 1);
psg->tmask[1] = (val & 2);
psg->tmask[2] = (val & 4);
psg->nmask[0] = (val & 8);
psg->nmask[1] = (val & 16);
psg->nmask[2] = (val & 32);
break;
case 8:
case 9:
case 10:
psg->volume[reg - 8] = val << 1;
break;
case 11:
case 12:
psg->env_freq = (psg->reg[12] << 8) + psg->reg[11];
break;
case 13:
psg->env_continue = (val >> 3) & 1;
psg->env_attack = (val >> 2) & 1;
psg->env_alternate = (val >> 1) & 1;
psg->env_hold = val & 1;
psg->env_face = psg->env_attack;
psg->env_pause = 0;
psg->env_count = 0x10000 - psg->env_freq;
psg->env_ptr = psg->env_face?0:0x1f;
break;
case 14:
case 15:
default:
break;
}
return;
}

View file

@ -0,0 +1,98 @@
/* SPDX-License-Identifier: MIT */
/* emu2149.h */
#ifndef EMU2149_H_
#define EMU2149_H_
#include "emutypes.h"
#define EMU2149_API
#define EMU2149_VOL_DEFAULT 1
#define EMU2149_VOL_YM2149 0
#define EMU2149_VOL_AY_3_8910 1
#define EMU2149_ZX_STEREO 0x80
#define PSG_MASK_CH(x) (1<<(x))
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct __PSG
{
/* Volume Table */
UINT32 *voltbl;
UINT8 reg[0x20];
INT32 out;
INT32 cout[3];
UINT32 clk, rate, base_incr, quality;
UINT32 count[3];
UINT32 volume[3];
UINT32 freq[3];
UINT32 edge[3];
UINT32 tmask[3];
UINT32 nmask[3];
UINT32 mask;
UINT32 stereo_mask[3];
UINT32 base_count;
UINT32 env_volume;
UINT32 env_ptr;
UINT32 env_face;
UINT32 env_continue;
UINT32 env_attack;
UINT32 env_alternate;
UINT32 env_hold;
UINT32 env_pause;
UINT32 env_reset;
UINT32 env_freq;
UINT32 env_count;
UINT32 noise_seed;
UINT32 noise_count;
UINT32 noise_freq;
/* rate converter */
UINT32 realstep;
UINT32 psgtime;
UINT32 psgstep;
INT32 prev, next;
INT32 sprev[2], snext[2];
/* I/O Ctrl */
UINT32 adr;
}
PSG;
EMU2149_API void PSG_set_quality (PSG * psg, UINT32 q);
EMU2149_API void PSG_set_clock(PSG * psg, UINT32 c);
EMU2149_API void PSG_set_rate (PSG * psg, UINT32 r);
EMU2149_API void PSG_init (PSG * psg, UINT32 clk, UINT32 rate);
EMU2149_API void PSG_reset (PSG *);
EMU2149_API void PSG_delete (PSG *);
EMU2149_API void PSG_writeReg (PSG *, UINT32 reg, UINT32 val);
EMU2149_API void PSG_writeIO (PSG * psg, UINT32 adr, UINT32 val);
EMU2149_API UINT8 PSG_readReg (PSG * psg, UINT32 reg);
EMU2149_API UINT8 PSG_readIO (PSG * psg);
EMU2149_API INT16 PSG_calc (PSG *);
EMU2149_API void PSG_calc_stereo (PSG * psg, INT32 **out, INT32 samples);
EMU2149_API void PSG_setFlags (PSG * psg, UINT8 flags);
EMU2149_API void PSG_setVolumeMode (PSG * psg, int type);
EMU2149_API UINT32 PSG_setMask (PSG *, UINT32 mask);
EMU2149_API UINT32 PSG_toggleMask (PSG *, UINT32 mask);
EMU2149_API void PSG_setStereoMask (PSG *psg, UINT32 mask);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,38 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBOPNMIDI_EMUTYPES_H
#define LIBOPNMIDI_EMUTYPES_H
#include <stdint.h>
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
typedef int8_t INT8;
typedef int16_t INT16;
typedef int32_t INT32;
typedef int64_t INT64;
#endif /* LIBOPNMIDI_EMUTYPES_H */

3987
thirdparty/opnmidi/chips/mamefm/fm.cpp vendored Normal file

File diff suppressed because it is too large Load diff

193
thirdparty/opnmidi/chips/mamefm/fm.h vendored Normal file
View file

@ -0,0 +1,193 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
/*
File: fm.h -- header file for software emulation for FM sound generator
*/
#ifndef MAME_SOUND_FM_H
#define MAME_SOUND_FM_H
#pragma once
#include "emu.h"
struct device_t;
typedef int32_t stream_sample_t;
/* --- select emulation chips --- */
#define BUILD_YM2203 (0) /* build YM2203(OPN) emulator */
#define BUILD_YM2608 (1) /* build YM2608(OPNA) emulator */
#define BUILD_YM2610 (0) /* build YM2610(OPNB) emulator */
#define BUILD_YM2610B (0) /* build YM2610B(OPNB?)emulator */
#define BUILD_YM2612 (0) /* build YM2612(OPN2) emulator */
#define BUILD_YM3438 (0) /* build YM3438(OPN) emulator */
/* select bit size of output : 8 or 16 */
#define FM_SAMPLE_BITS 16
/* select timer system internal or external */
#define FM_INTERNAL_TIMER 0
/* --- speedup optimize --- */
/* busy flag emulation , The definition of FM_GET_TIME_NOW() is necessary. */
#define FM_BUSY_FLAG_SUPPORT 0
/* --- external SSG(YM2149/AY-3-8910)emulator interface port */
/* used by YM2203,YM2608,and YM2610 */
struct ssg_callbacks
{
void (*set_clock)(device_t *device, int clock);
void (*write)(device_t *device, int address, int data);
int (*read)(device_t *device);
void (*reset)(device_t *device);
};
/* --- external callback functions for realtime update --- */
#if FM_BUSY_FLAG_SUPPORT
using TIME_TYPE = attotime;
#define UNDEFINED_TIME attotime::zero
#define FM_GET_TIME_NOW(machine) (machine)->time()
inline TIME_TYPE ADD_TIMES(TIME_TYPE const &t1, TIME_TYPE const &t2) { return t1 + t2; }
constexpr int COMPARE_TIMES(TIME_TYPE const &t1, TIME_TYPE const &t2) { return (t1 == t2) ? 0 : (t1 < t2) ? -1 : 1; }
template <typename X> constexpr TIME_TYPE MULTIPLY_TIME_BY_INT(TIME_TYPE const &t, X &&i) { return t * i; }
#endif
typedef stream_sample_t FMSAMPLE;
/*
#if (FM_SAMPLE_BITS==16)
typedef int16_t FMSAMPLE;
#endif
#if (FM_SAMPLE_BITS==8)
typedef unsigned char FMSAMPLE;
#endif
*/
typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset);
typedef void(*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data);
typedef void (*FM_TIMERHANDLER)(device_t *device,int c,int cnt,int clock);
typedef void (*FM_IRQHANDLER)(device_t *device,int irq);
/* FM_TIMERHANDLER : Stop or Start timer */
/* int n = chip number */
/* int c = Channel 0=TimerA,1=TimerB */
/* int count = timer count (0=stop) */
/* doube stepTime = step time of one count (sec.)*/
/* FM_IRQHHANDLER : IRQ level changing sense */
/* int n = chip number */
/* int irq = IRQ level 0=OFF,1=ON */
#if BUILD_YM2203
/* -------------------- YM2203(OPN) Interface -------------------- */
/*
** Initialize YM2203 emulator(s).
**
** 'num' is the number of virtual YM2203's to allocate
** 'baseclock'
** 'rate' is sampling rate
** 'TimerHandler' timer callback handler when timer start and clear
** 'IRQHandler' IRQ callback handler when changed IRQ level
** return 0 = success
*/
void * ym2203_init(device_t *device, int baseclock, int rate,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
/*
** YM2203 clock changed notifier
*/
void ym2203_clock_changed(void *chip, int clock, int rate);
/*
** shutdown the YM2203 emulators
*/
void ym2203_shutdown(void *chip);
/*
** reset all chip registers for YM2203 number 'num'
*/
void ym2203_reset_chip(void *chip);
/*
** update one of chip
*/
void ym2203_update_one(void *chip, FMSAMPLE *buffer, int length);
/*
** Write
** return : InterruptLevel
*/
int ym2203_write(void *chip,int a,unsigned char v);
/*
** Read
** return : InterruptLevel
*/
unsigned char ym2203_read(void *chip,int a);
/*
** Timer OverFlow
*/
int ym2203_timer_over(void *chip, int c);
/*
** State Save
*/
void ym2203_postload(void *chip);
#endif /* BUILD_YM2203 */
#if BUILD_YM2608
/* -------------------- YM2608(OPNA) Interface -------------------- */
void * ym2608_init(device_t *device, int baseclock, int rate,
FM_READBYTE InternalReadByte,
FM_READBYTE ExternalReadByte, FM_WRITEBYTE ExternalWriteByte,
FM_TIMERHANDLER TimerHandler, FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
void ym2608_clock_changed(void *chip, int clock, int rate);
void ym2608_shutdown(void *chip);
void ym2608_reset_chip(void *chip);
void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length);
int ym2608_write(void *chip, int a,unsigned char v);
void ym2608_write_pan(void *chip, int c,unsigned char v); // libOPNMIDI: soft panning
unsigned char ym2608_read(void *chip,int a);
int ym2608_timer_over(void *chip, int c );
void ym2608_postload(void *chip);
#endif /* BUILD_YM2608 */
#if (BUILD_YM2610||BUILD_YM2610B)
/* -------------------- YM2610(OPNB) Interface -------------------- */
void * ym2610_init(device_t *device, int baseclock, int rate,
FM_READBYTE adpcm_a_read_byte, FM_READBYTE adpcm_b_read_byte,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
void ym2610_clock_changed(void *chip, int clock, int rate);
void ym2610_shutdown(void *chip);
void ym2610_reset_chip(void *chip);
void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length);
#if BUILD_YM2610B
void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length);
#endif /* BUILD_YM2610B */
int ym2610_write(void *chip, int a,unsigned char v);
unsigned char ym2610_read(void *chip,int a);
int ym2610_timer_over(void *chip, int c );
void ym2610_postload(void *chip);
#endif /* (BUILD_YM2610||BUILD_YM2610B) */
#if (BUILD_YM2612||BUILD_YM3438)
void * ym2612_init(device_t *device, int baseclock, int rate,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);
void ym2612_clock_changed(void *chip, int clock, int rate);
void ym2612_shutdown(void *chip);
void ym2612_reset_chip(void *chip);
void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length);
int ym2612_write(void *chip, int a,unsigned char v);
unsigned char ym2612_read(void *chip,int a);
int ym2612_timer_over(void *chip, int c );
void ym2612_postload(void *chip);
#endif /* (BUILD_YM2612||BUILD_YM3438) */
#endif // MAME_SOUND_FM_H

View file

@ -0,0 +1,543 @@
/*
This data is derived from the chip's output - internal ROM can't be read.
It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal.
*/
static const unsigned char YM2608_ADPCM_ROM[0x2000] = {
/* Source: 01BD.ROM */
/* Length: 448 / 0x000001C0 */
0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54,
0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97,
0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0,
0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1,
0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24,
0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99,
0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99,
0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09,
0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8,
0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39,
0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9,
0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A,
0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40,
0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00,
0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12,
0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18,
0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52,
0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02,
0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B,
0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC,
0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A,
0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98,
0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A,
0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99,
0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99,
0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11,
0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31,
0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
/* Source: 02SD.ROM */
/* Length: 640 / 0x00000280 */
0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1,
0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41,
0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03,
0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18,
0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02,
0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92,
0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28,
0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62,
0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E,
0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09,
0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB,
0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83,
0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85,
0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21,
0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0,
0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80,
0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21,
0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8,
0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F,
0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A,
0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12,
0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36,
0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80,
0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90,
0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99,
0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03,
0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89,
0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18,
0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27,
0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9,
0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39,
0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23,
0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1,
0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3,
0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9,
0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71,
0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A,
0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88,
0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C,
0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10,
/* Source: 04TOP.ROM */
/* Length: 5952 / 0x00001740 */
0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C,
0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00,
0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E,
0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C,
0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38,
0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81,
0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A,
0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81,
0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4,
0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8,
0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10,
0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29,
0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98,
0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59,
0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3,
0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28,
0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B,
0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B,
0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11,
0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0,
0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90,
0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00,
0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8,
0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29,
0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F,
0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12,
0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8,
0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4,
0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82,
0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11,
0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09,
0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A,
0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3,
0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85,
0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88,
0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80,
0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C,
0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10,
0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81,
0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81,
0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8,
0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A,
0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79,
0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49,
0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA,
0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99,
0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24,
0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7,
0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8,
0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0,
0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15,
0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18,
0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B,
0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C,
0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B,
0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7,
0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14,
0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02,
0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98,
0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88,
0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01,
0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2,
0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D,
0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19,
0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28,
0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21,
0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A,
0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2,
0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87,
0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11,
0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C,
0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A,
0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0,
0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1,
0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11,
0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90,
0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00,
0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2,
0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3,
0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82,
0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00,
0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B,
0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28,
0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79,
0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90,
0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1,
0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05,
0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10,
0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19,
0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D,
0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5,
0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90,
0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49,
0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C,
0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C,
0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0,
0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3,
0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4,
0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4,
0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9,
0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01,
0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88,
0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12,
0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D,
0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02,
0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09,
0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89,
0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A,
0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2,
0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49,
0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50,
0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80,
0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C,
0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C,
0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13,
0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01,
0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18,
0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C,
0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1,
0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3,
0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B,
0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E,
0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90,
0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28,
0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28,
0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E,
0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91,
0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00,
0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71,
0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20,
0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8,
0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03,
0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7,
0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C,
0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09,
0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12,
0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2,
0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6,
0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02,
0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B,
0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC,
0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0,
0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00,
0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A,
0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2,
0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3,
0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F,
0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A,
0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0,
0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84,
0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10,
0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21,
0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F,
0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B,
0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24,
0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3,
0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00,
0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93,
0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10,
0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30,
0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0,
0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7,
0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1,
0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19,
0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B,
0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99,
0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A,
0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C,
0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03,
0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1,
0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A,
0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19,
0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8,
0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11,
0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A,
0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59,
0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F,
0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C,
0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80,
0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5,
0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08,
0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29,
0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1,
0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28,
0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D,
0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A,
0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D,
0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48,
0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A,
0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3,
0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1,
0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18,
0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C,
0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A,
0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19,
0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91,
0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04,
0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29,
0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10,
0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1,
0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0,
0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84,
0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95,
0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11,
0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B,
0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19,
0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10,
0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1,
0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17,
0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A,
0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08,
0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C,
0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1,
0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2,
0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09,
0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20,
0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A,
0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D,
0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82,
0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1,
0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2,
0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99,
0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18,
0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1,
0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1,
0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01,
0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40,
0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A,
0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C,
0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83,
0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12,
0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09,
0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68,
0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92,
0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A,
0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80,
0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82,
0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80,
0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B,
0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B,
0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18,
0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91,
0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1,
0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6,
0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B,
0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89,
0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90,
0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A,
0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01,
0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98,
0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29,
0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D,
0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92,
0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93,
0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88,
0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79,
0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20,
0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59,
0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9,
0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8,
0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07,
0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13,
0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80,
0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB,
0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A,
0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20,
0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0,
0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20,
0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39,
0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92,
0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1,
0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0,
0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41,
0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC,
0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30,
0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18,
0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B,
0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A,
0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09,
0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10,
0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1,
0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96,
0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09,
0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09,
0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A,
0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B,
0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90,
0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0,
0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1,
0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28,
0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18,
0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF,
0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0,
0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02,
0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08,
0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96,
0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88,
0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28,
0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D,
0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0,
0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83,
0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3,
0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04,
0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2,
0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21,
0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98,
0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8,
0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23,
0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39,
0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08,
0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E,
0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14,
0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4,
0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0,
0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88,
0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0,
0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69,
0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08,
0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13,
0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0,
0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA,
0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81,
0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01,
0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B,
0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B,
0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4,
0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18,
0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01,
0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0,
0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5,
0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94,
0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A,
0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A,
0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88,
0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B,
0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01,
0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12,
0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12,
0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D,
0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02,
0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1,
0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82,
0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1,
0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A,
0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A,
0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F,
0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98,
0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1,
0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3,
0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68,
0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91,
0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A,
0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C,
0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90,
0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6,
0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89,
0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B,
0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA,
0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1,
0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93,
0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93,
0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00,
0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B,
0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2,
0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92,
0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89,
0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48,
0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10,
0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29,
0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91,
0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3,
0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90,
/* Source: 08HH.ROM */
/* Length: 384 / 0x00000180 */
0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3,
0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7,
0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D,
0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80,
0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B,
0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2,
0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91,
0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D,
0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C,
0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5,
0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B,
0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01,
0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92,
0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93,
0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4,
0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3,
0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08,
0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3,
0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88,
0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92,
0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00,
0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4,
0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4,
0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0,
/* Source: 10TOM.ROM */
/* Length: 640 / 0x00000280 */
0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2,
0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11,
0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1,
0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32,
0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9,
0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01,
0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14,
0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA,
0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81,
0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00,
0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63,
0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB,
0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34,
0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB,
0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31,
0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99,
0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A,
0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30,
0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02,
0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98,
0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11,
0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11,
0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A,
0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35,
0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11,
0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB,
0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42,
0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99,
0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20,
0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81,
0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31,
0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC,
0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33,
0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB,
0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31,
0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99,
0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99,
0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88,
0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12,
0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
/* Source: 20RIM.ROM */
/* Length: 128 / 0x00000080 */
0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A,
0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2,
0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C,
0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91,
0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59,
0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92,
0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6,
0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80,
};

View file

@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "resampler.hpp"
enum Stereo
{
LEFT = 0,
RIGHT = 1
};
static const size_t SMPL_BUF_SIZE_ = 0x10000;
namespace chip
{
AbstractResampler::AbstractResampler()
{
for (int pan = LEFT; pan <= RIGHT; ++pan) {
destBuf_[pan] = new sample[SMPL_BUF_SIZE_]();
}
}
AbstractResampler::~AbstractResampler()
{
for (int pan = LEFT; pan <= RIGHT; ++pan) {
delete[] destBuf_[pan];
}
}
void AbstractResampler::init(int srcRate, int destRate, size_t maxDuration)
{
srcRate_ = srcRate;
maxDuration_ = maxDuration;
destRate_ = destRate;
updateRateRatio();
}
void AbstractResampler::setDestributionRate(int destRate)
{
destRate_ = destRate;
updateRateRatio();
}
void AbstractResampler::setMaxDuration(size_t maxDuration)
{
maxDuration_ = maxDuration;
}
/****************************************/
sample** LinearResampler::interpolate(sample** src, size_t nSamples, size_t intrSize)
{
(void)intrSize;
// Linear interplation
for (int pan = LEFT; pan <= RIGHT; ++pan) {
for (size_t n = 0; n < nSamples; ++n) {
float curnf = n * rateRatio_;
int curni = static_cast<int>(curnf);
float sub = curnf - curni;
if (sub) {
destBuf_[pan][n] = static_cast<sample>(src[pan][curni] + (src[pan][curni + 1] - src[pan][curni]) * sub);
}
else /* if (sub == 0) */ {
destBuf_[pan][n] = src[pan][curni];
}
}
}
return destBuf_;
}
/****************************************/
const float SincResampler::F_PI_ = 3.14159265f;
const int SincResampler::SINC_OFFSET_ = 16;
void SincResampler::init(int srcRate, int destRate, size_t maxDuration)
{
AbstractResampler::init(srcRate, destRate, maxDuration);
initSincTables();
}
void SincResampler::setDestributionRate(int destRate)
{
AbstractResampler::setDestributionRate(destRate);
initSincTables();
}
void SincResampler::setMaxDuration(size_t maxDuration)
{
AbstractResampler::setMaxDuration(maxDuration);
initSincTables();
}
sample** SincResampler::interpolate(sample** src, size_t nSamples, size_t intrSize)
{
// Sinc interpolation
size_t offsetx2 = SINC_OFFSET_ << 1;
for (int pan = LEFT; pan <= RIGHT; ++pan) {
for (size_t n = 0; n < nSamples; ++n) {
size_t seg = n * offsetx2;
int curn = static_cast<int>(n * rateRatio_);
int k = curn - SINC_OFFSET_;
if (k < 0) k = 0;
int end = curn + SINC_OFFSET_;
if (static_cast<size_t>(end) > intrSize) end = static_cast<int>(intrSize);
sample samp = 0;
for (; k < end; ++k) {
samp += static_cast<sample>(src[pan][k] * sincTable_[seg + SINC_OFFSET_ + (k - curn)]);
}
destBuf_[pan][n] = samp;
}
}
return destBuf_;
}
void SincResampler::initSincTables()
{
size_t maxSamples = destRate_ * maxDuration_ / 1000;
if (srcRate_ != destRate_) {
size_t intrSize = calculateInternalSampleSize(maxSamples);
size_t offsetx2 = SINC_OFFSET_ << 1;
sincTable_.resize(maxSamples * offsetx2);
for (size_t n = 0; n < maxSamples; ++n) {
size_t seg = n * offsetx2;
float rcurn = n * rateRatio_;
int curn = static_cast<int>(rcurn);
int k = curn - SINC_OFFSET_;
if (k < 0) k = 0;
int end = curn + SINC_OFFSET_;
if (static_cast<size_t>(end) > intrSize) end = static_cast<int>(intrSize);
for (; k < end; ++k) {
sincTable_[seg + SINC_OFFSET_ + (k - curn)] = sinc(F_PI_ * (rcurn - k));
}
}
}
}
}

View file

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-only
#pragma once
#include <vector>
#include <cmath>
#include <cstddef>
#include <stdint.h>
typedef int32_t sample;
namespace chip
{
class AbstractResampler
{
public:
AbstractResampler();
virtual ~AbstractResampler();
virtual void init(int srcRate, int destRate, size_t maxDuration);
virtual void setDestributionRate(int destRate);
virtual void setMaxDuration(size_t maxDuration);
virtual sample** interpolate(sample** src, size_t nSamples, size_t intrSize) = 0;
inline size_t calculateInternalSampleSize(size_t nSamples)
{
float f = nSamples * rateRatio_;
size_t i = static_cast<size_t>(f);
return ((f - i) ? (i + 1) : i);
}
protected:
inline void updateRateRatio() {
rateRatio_ = static_cast<float>(srcRate_) / destRate_;
}
protected:
int srcRate_, destRate_;
size_t maxDuration_;
float rateRatio_;
sample* destBuf_[2];
};
class LinearResampler : public AbstractResampler
{
public:
sample** interpolate(sample** src, size_t nSamples, size_t intrSize);
};
class SincResampler : public AbstractResampler
{
public:
void init(int srcRate, int destRate, size_t maxDuration);
void setDestributionRate(int destRate);
void setMaxDuration(size_t maxDuration);
sample** interpolate(sample** src, size_t nSamples, size_t intrSize);
private:
std::vector<float> sincTable_;
static const float F_PI_;
static const int SINC_OFFSET_;
void initSincTables();
static inline float sinc(float x)
{
return ((!x) ? 1.0f : (std::sin(x) / x));
}
};
}

View file

@ -0,0 +1,628 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski
/*
**
** File: ymdeltat.c
**
** YAMAHA DELTA-T adpcm sound emulation subroutine
** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B)
**
** Base program is YM2610 emulator by Hiromitsu Shioya.
** Written by Tatsuyuki Satoh
** Improvements by Jarek Burczynski (bujar at mame dot net)
**
**
** History:
**
** 03-08-2003 Jarek Burczynski:
** - fixed BRDY flag implementation.
**
** 24-07-2003 Jarek Burczynski, Frits Hilderink:
** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset
**
** 22-07-2003 Jarek Burczynski, Frits Hilderink:
** - fixed external memory support
**
** 15-06-2003 Jarek Burczynski:
** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08)
** - implemented support for the Limit address register
** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM
** - implemented external memory access (read/write) via the ADPCM data reg reads/writes
** Thanks go to Frits Hilderink for the example code.
**
** 14-06-2003 Jarek Burczynski:
** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO
** - modified EOS handling
**
** 05-04-2003 Jarek Burczynski:
** - implemented partial support for external/processor memory on sample replay
**
** 01-12-2002 Jarek Burczynski:
** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi
** - renamed/removed some YM_DELTAT struct fields
**
** 28-12-2001 Acho A. Tang
** - added EOS status report on ADPCM playback.
**
** 05-08-2001 Jarek Burczynski:
** - now_step is initialized with 0 at the start of play.
**
** 12-06-2001 Jarek Burczynski:
** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC.
** Checked on real YM2610 chip - address register is 24 bits wide.
** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem.
**
** TO DO:
** Check size of the address register on the other chips....
**
** Version 0.72
**
** sound chips that have this unit:
** YM2608 OPNA
** YM2610/B OPNB
** Y8950 MSX AUDIO
**
*/
#include "emu.h"
#include "ymdeltat.h"
#define YM_DELTAT_SHIFT (16)
#define YM_DELTAT_DELTA_MAX (24576)
#define YM_DELTAT_DELTA_MIN (127)
#define YM_DELTAT_DELTA_DEF (127)
#define YM_DELTAT_DECODE_RANGE 32768
#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))
#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)
/* Forecast to next Forecast (rate = *8) */
/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */
static CONSTEXPR int32_t ym_deltat_decode_tableB1[16] = {
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9, -11, -13, -15,
};
/* delta to next delta (rate= *64) */
/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */
static CONSTEXPR int32_t ym_deltat_decode_tableB2[16] = {
57, 57, 57, 57, 77, 102, 128, 153,
57, 57, 57, 57, 77, 102, 128, 153
};
#if 0
void YM_DELTAT::BRDY_callback()
{
logerror("BRDY_callback reached (flag set) !\n");
/* set BRDY bit in status register */
if(status_set_handler)
if(status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
#endif
uint8_t YM_DELTAT::ADPCM_Read()
{
uint8_t v = 0;
/* external memory read */
if ((portstate & 0xe0) == 0x20)
{
/* two dummy reads */
if (memread)
{
now_addr = start << 1;
memread--;
return 0;
}
if (now_addr != (end << 1))
{
v = read_byte(device, now_addr>>1);
/*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/
now_addr += 2; /* two nibbles at a time */
/* reset BRDY bit in status register, which means we are reading the memory now */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
/* setup a timer that will callback us in 10 master clock cycles for Y8950
* in the callback set the BRDY flag to 1 , which means we have another data ready.
* For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
*/
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
else
{
/* set EOS bit in status register */
if (status_set_handler && status_change_EOS_bit)
(status_set_handler)(status_change_which_chip, status_change_EOS_bit);
}
}
return v;
}
/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
static CONSTEXPR uint8_t dram_rightshift[4]={3,0,0,0};
/* DELTA-T ADPCM write register */
void YM_DELTAT::ADPCM_Write(int r, int v)
{
if (r >= 0x10) return;
reg[r] = v; /* stock data */
switch (r)
{
case 0x00:
/*
START:
Accessing *external* memory is started when START bit (D7) is set to "1", so
you must set all conditions needed for recording/playback before starting.
If you access *CPU-managed* memory, recording/playback starts after
read/write of ADPCM data register $08.
REC:
0 = ADPCM synthesis (playback)
1 = ADPCM analysis (record)
MEMDATA:
0 = processor (*CPU-managed*) memory (means: using register $08)
1 = external memory (using start/end/limit registers to access memory: RAM or ROM)
SPOFF:
controls output pin that should disable the speaker while ADPCM analysis
RESET and REPEAT only work with external memory.
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
/* handle emulation mode */
if (emulation_mode == EMULATION_MODE_YM2610)
{
v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */
v &= ~0x40; /* YM2610 has no rec bit */
}
portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */
if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
{
/* set PCM BUSY bit */
PCM_BSY = 1;
/* start ADPCM */
now_step = 0;
acc = 0;
prev_acc = 0;
adpcml = 0;
adpcmd = YM_DELTAT_DELTA_DEF;
now_data = 0;
}
if (portstate & 0x20) /* do we access external memory? */
{
now_addr = start << 1;
memread = 2; /* two dummy reads needed before accesing external memory via register $08*/
}
else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */
{
now_addr = 0;
}
if (portstate & 0x01)
{
portstate = 0x00;
/* clear PCM BUSY bit (in status register) */
PCM_BSY = 0;
/* set BRDY flag */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
break;
case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
/* handle emulation mode */
if (emulation_mode == EMULATION_MODE_YM2610)
{
v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */
}
pan = &output_pointer[(v >> 6) & 0x03];
if ((control2 & 3) != (v & 3))
{
/*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
if (DRAMportshift != dram_rightshift[v & 3])
{
DRAMportshift = dram_rightshift[v & 3];
/* final shift value depends on chip type and memory type selected:
8 for YM2610 (ROM only),
5 for ROM for Y8950 and YM2608,
5 for x8bit DRAMs for Y8950 and YM2608,
2 for x1bit DRAMs for Y8950 and YM2608.
*/
/* refresh addresses */
start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift);
end += (1 << (portshift - DRAMportshift)) - 1;
limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift);
}
}
control2 = v;
break;
case 0x02: /* Start Address L */
case 0x03: /* Start Address H */
start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
/*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/
break;
case 0x04: /* Stop Address L */
case 0x05: /* Stop Address H */
end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift);
end += (1 << (portshift - DRAMportshift)) - 1;
/*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/
break;
case 0x06: /* Prescale L (ADPCM and Record frq) */
case 0x07: /* Prescale H */
break;
case 0x08: /* ADPCM data */
/*
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
/* external memory write */
if ((portstate & 0xe0) == 0x60)
{
if (memread)
{
now_addr = start << 1;
memread = 0;
}
/*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/
if (now_addr != (end << 1))
{
write_byte(device, now_addr >> 1, v);
now_addr += 2; /* two nybbles at a time */
/* reset BRDY bit in status register, which means we are processing the write */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
/* setup a timer that will callback us in 10 master clock cycles for Y8950
* in the callback set the BRDY flag to 1 , which means we have written the data.
* For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
*/
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
else
{
/* set EOS bit in status register */
if (status_set_handler && status_change_EOS_bit)
(status_set_handler)(status_change_which_chip, status_change_EOS_bit);
}
return;
}
/* ADPCM synthesis from CPU */
if ((portstate & 0xe0) == 0x80)
{
CPU_data = v;
/* Reset BRDY bit in status register, which means we are full of data */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
return;
}
break;
case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
case 0x0a: /* DELTA-N H */
delta = (reg[0xa] * 0x0100 | reg[0x9]);
step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase);
/*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/
break;
case 0x0b: /* Output level control (volume, linear) */
{
const int32_t oldvol = volume;
volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE;
/* v * ((1<<16)>>8) >> 15;
* thus: v * (1<<8) >> 15;
* thus: output_range must be (1 << (15+8)) at least
* v * ((1<<23)>>8) >> 15;
* v * (1<<15) >> 15;
*/
/*logerror("DELTAT vol = %2x\n",v&0xff);*/
if (oldvol != 0)
{
adpcml = int(double(adpcml) / double(oldvol) * double(volume));
}
}
break;
case 0x0c: /* Limit Address L */
case 0x0d: /* Limit Address H */
limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift);
/*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/
break;
}
}
void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev)
{
device = dev;
now_addr = 0;
now_step = 0;
step = 0;
start = 0;
end = 0;
limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */
volume = 0;
pan = &output_pointer[panidx];
acc = 0;
prev_acc = 0;
adpcmd = 127;
adpcml = 0;
emulation_mode = uint8_t(mode);
portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0;
control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */
DRAMportshift = dram_rightshift[control2 & 3];
/* The flag mask register disables the BRDY after the reset, however
** as soon as the mask is enabled the flag needs to be set. */
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
void YM_DELTAT::postload(uint8_t *regs)
{
/* to keep adpcml */
volume = 0;
/* update */
for (int r = 1; r < 16; r++)
ADPCM_Write(r, regs[r]);
reg[0] = regs[0];
/* current rom data */
now_data = read_byte(device, now_addr >> 1);
}
void YM_DELTAT::savestate(device_t *device)
{
(void)device;
#ifdef MAME_EMU_SAVE_H
YM_DELTAT *const DELTAT = this; // makes the save name sensible
device->save_item(NAME(DELTAT->portstate));
device->save_item(NAME(DELTAT->now_addr));
device->save_item(NAME(DELTAT->now_step));
device->save_item(NAME(DELTAT->acc));
device->save_item(NAME(DELTAT->prev_acc));
device->save_item(NAME(DELTAT->adpcmd));
device->save_item(NAME(DELTAT->adpcml));
#endif
}
#define YM_DELTAT_Limit(val,max,min) \
{ \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)
{
uint32_t step;
int data;
DELTAT->now_step += DELTAT->step;
if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if ( DELTAT->now_addr == (DELTAT->limit<<1) )
DELTAT->now_addr = 0;
if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */
if( DELTAT->portstate&0x10 ){
/* repeat start */
DELTAT->now_addr = DELTAT->start<<1;
DELTAT->acc = 0;
DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
DELTAT->prev_acc = 0;
}else{
/* set EOS bit in status register */
if(DELTAT->status_set_handler)
if(DELTAT->status_change_EOS_bit)
(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
/* clear PCM BUSY bit (reflected in status register) */
DELTAT->PCM_BSY = 0;
DELTAT->portstate = 0;
DELTAT->adpcml = 0;
DELTAT->prev_acc = 0;
return;
}
}
if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f;
else
{
DELTAT->now_data = DELTAT->read_byte(DELTAT->device, DELTAT->now_addr>>1);
data = DELTAT->now_data >> 4;
}
DELTAT->now_addr++;
/* 12-06-2001 JB: */
/* YM2610 address register is 24 bits wide.*/
/* The "+1" is there because we use 1 bit more for nibble calculations.*/
/* WARNING: */
/* Side effect: we should take the size of the mapped ROM into account */
DELTAT->now_addr &= ( (1<<(24+1))-1);
/* store accumulator value */
DELTAT->prev_acc = DELTAT->acc;
/* Forecast to next Forecast */
DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
/* delta to next delta */
DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
/* ElSemi: Fix interpolator. */
/*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/
}while(--step);
}
/* ElSemi: Fix interpolator. */
DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
/* output for work of output channels (outd[OPNxxxx])*/
*(DELTAT->pan) += DELTAT->adpcml;
}
static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)
{
uint32_t step;
int data;
DELTAT->now_step += DELTAT->step;
if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if( DELTAT->now_addr&1 )
{
data = DELTAT->now_data & 0x0f;
DELTAT->now_data = DELTAT->CPU_data;
/* after we used CPU_data, we set BRDY bit in status register,
* which means we are ready to accept another byte of data */
if(DELTAT->status_set_handler)
if(DELTAT->status_change_BRDY_bit)
(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
}
else
{
data = DELTAT->now_data >> 4;
}
DELTAT->now_addr++;
/* store accumulator value */
DELTAT->prev_acc = DELTAT->acc;
/* Forecast to next Forecast */
DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
/* delta to next delta */
DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
}while(--step);
}
/* ElSemi: Fix interpolator. */
DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
/* output for work of output channels (outd[OPNxxxx])*/
*(DELTAT->pan) += DELTAT->adpcml;
}
/* ADPCM B (Delta-T control type) */
void YM_DELTAT::ADPCM_CALC()
{
/*
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
if ( (portstate & 0xe0)==0xa0 )
{
YM_DELTAT_synthesis_from_external_memory(this);
return;
}
if ( (portstate & 0xe0)==0x80 )
{
/* ADPCM synthesis from CPU-managed memory (from reg $08) */
YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */
return;
}
//todo: ADPCM analysis
// if ( (portstate & 0xe0)==0xc0 )
// if ( (portstate & 0xe0)==0xe0 )
return;
}

View file

@ -0,0 +1,91 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski
#ifndef MAME_SOUND_YMDELTAT_H
#define MAME_SOUND_YMDELTAT_H
#pragma once
#include "emu.h"
typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset);
typedef void (*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data);
typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits);
/* DELTA-T (adpcm type B) struct */
struct YM_DELTAT {/* AT: rearranged and tightened structure */
enum
{
EMULATION_MODE_NORMAL = 0,
EMULATION_MODE_YM2610 = 1
};
FM_READBYTE read_byte;
FM_WRITEBYTE write_byte;
int32_t *output_pointer;/* pointer of output pointers */
int32_t *pan; /* pan : &output_pointer[pan] */
double freqbase;
#if 0
double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */
double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */
#endif
uint32_t memory_size;
int output_range;
uint32_t now_addr; /* current address */
uint32_t now_step; /* correct step */
uint32_t step; /* step */
uint32_t start; /* start address */
uint32_t limit; /* limit address */
uint32_t end; /* end address */
uint32_t delta; /* delta scale */
int32_t volume; /* current volume */
int32_t acc; /* shift Measurement value*/
int32_t adpcmd; /* next Forecast */
int32_t adpcml; /* current value */
int32_t prev_acc; /* leveling value */
uint8_t now_data; /* current rom data */
uint8_t CPU_data; /* current data from reg 08 */
uint8_t portstate; /* port status */
uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */
uint8_t portshift; /* address bits shift-left:
** 8 for YM2610,
** 5 for Y8950 and YM2608 */
uint8_t DRAMportshift; /* address bits shift-right:
** 0 for ROM and x8bit DRAMs,
** 3 for x1 DRAMs */
uint8_t memread; /* needed for reading/writing external memory */
/* handlers and parameters for the status flags support */
STATUS_CHANGE_HANDLER status_set_handler;
STATUS_CHANGE_HANDLER status_reset_handler;
/* note that different chips have these flags on different
** bits of the status register
*/
void * status_change_which_chip; /* this chip id */
uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/
uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */
uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */
/* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above,
** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608)
*/
uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */
uint8_t reg[16]; /* adpcm registers */
uint8_t emulation_mode; /* which chip we're emulating */
device_t *device;
/*void BRDY_callback();*/
uint8_t ADPCM_Read();
void ADPCM_Write(int r, int v);
void ADPCM_Reset(int panidx, int mode, device_t *dev);
void ADPCM_CALC();
void postload(uint8_t *regs);
void savestate(device_t *device);
};
#endif // MAME_SOUND_YMDELTAT_H

19
thirdparty/opnmidi/chips/np2/compiler.h vendored Normal file
View file

@ -0,0 +1,19 @@
#ifndef OPNMIDI_FMGEN_COMPILER_H
#define OPNMIDI_FMGEN_COMPILER_H
#if defined(_MSC_VER) && !defined(FASTCALL)
#define FASTCALL __fastcall
#elif defined(__GNUC__) && !defined(FASTCALL)
#define FASTCALL __attribute__((regparm(2)))
#endif
#ifndef FASTCALL
#define FASTCALL
#endif
#define SOUNDCALL FASTCALL
#ifndef MAX_PATH
#define MAX_PATH 256
#endif
#endif

View file

@ -0,0 +1,15 @@
#ifndef incl_diag_h
#define incl_diag_h
#define LOG0(m) void (0)
#define LOG1(m,a) void (0)
#define LOG2(m,a,b) void (0)
#define LOG3(m,a,b,c) void (0)
#define LOG4(m,a,b,c,d) void (0)
#define LOG5(m,a,b,c,d,e) void (0)
#define LOG6(m,a,b,c,d,e,f) void (0)
#define LOG7(m,a,b,c,d,e,f,g) void (0)
#define LOG8(m,a,b,c,d,e,f,g,h) void (0)
#define LOG9(m,a,b,c,d,e,f,g,h,i) void (0)
#endif // incl_diag_h

View file

@ -0,0 +1,177 @@
// $Id: file.cpp,v 1.6 1999/12/28 11:14:05 cisc Exp $
#include <stdio.h>
#include "fmgen_types.h"
#include "fmgen_headers.h"
#include "fmgen_file.h"
// ---------------------------------------------------------------------------
// 構築/消滅
// ---------------------------------------------------------------------------
FileIO::FileIO()
{
flags = 0;
}
FileIO::FileIO(const char* filename, uint flg)
{
flags = 0;
Open(filename, flg);
}
FileIO::~FileIO()
{
Close();
}
// ---------------------------------------------------------------------------
// ファイルを開く
// ---------------------------------------------------------------------------
bool FileIO::Open(const char* filename, uint flg)
{
char mode[5] = "rwb";
Close();
strncpy(path, filename, MAX_PATH);
if(flg & readonly)
strcpy(mode, "rb");
else {
if(flg & create)
strcpy(mode, "rwb+");
else
strcpy(mode, "rwb");
}
pfile = fopen(filename, mode);
flags = (flg & readonly) | (pfile == NULL ? 0 : open);
if (pfile == NULL)
error = file_not_found;
SetLogicalOrigin(0);
return !(pfile == NULL);
}
// ---------------------------------------------------------------------------
// ファイルがない場合は作成
// ---------------------------------------------------------------------------
bool FileIO::CreateNew(const char* filename)
{
uint flg = create;
return Open(filename, flg);
}
// ---------------------------------------------------------------------------
// ファイルを作り直す
// ---------------------------------------------------------------------------
bool FileIO::Reopen(uint flg)
{
if (!(flags & open)) return false;
if ((flags & readonly) && (flg & create)) return false;
if (flags & readonly) flg |= readonly;
return Open(path, flg);
}
// ---------------------------------------------------------------------------
// ファイルを閉じる
// ---------------------------------------------------------------------------
void FileIO::Close()
{
if (GetFlags() & open)
{
fclose(pfile);
flags = 0;
}
}
// ---------------------------------------------------------------------------
// ファイル殻の読み出し
// ---------------------------------------------------------------------------
int32 FileIO::Read(void* dest, int32 size)
{
if (!(GetFlags() & open))
return -1;
size_t readsize;
if (!(readsize = fread(dest, 1, static_cast<size_t>(size), pfile)))
return -1;
return size;
}
// ---------------------------------------------------------------------------
// ファイルへの書き出し
// ---------------------------------------------------------------------------
int32 FileIO::Write(const void* dest, int32 size)
{
if (!(GetFlags() & open) || (GetFlags() & readonly))
return -1;
size_t writtensize;
if (!(writtensize = fwrite(dest, 1, static_cast<size_t>(size), pfile)))
return -1;
return static_cast<int32>(writtensize);
}
// ---------------------------------------------------------------------------
// ファイルをシーク
// ---------------------------------------------------------------------------
bool FileIO::Seek(int32 pos, SeekMethod method)
{
if (!(GetFlags() & open))
return false;
int origin;
switch (method)
{
case begin:
origin = SEEK_SET;
break;
case current:
origin = SEEK_CUR;
break;
case end:
origin = SEEK_END;
break;
default:
return false;
}
return (fseek(pfile, pos, origin) != 0);
}
// ---------------------------------------------------------------------------
// ファイルの位置を得る
// ---------------------------------------------------------------------------
int32 FileIO::Tellp()
{
if (!(GetFlags() & open))
return 0;
return static_cast<int32>(ftell(pfile));
}
// ---------------------------------------------------------------------------
// 現在の位置をファイルの終端とする
// ---------------------------------------------------------------------------
bool FileIO::SetEndOfFile()
{
if (!(GetFlags() & open))
return false;
return Seek(0, end);
}

View file

@ -0,0 +1,64 @@
// $Id: file.h,v 1.6 1999/11/26 10:14:09 cisc Exp $
#if !defined(win32_file_h)
#define win32_file_h
#include "fmgen_types.h"
// ---------------------------------------------------------------------------
class FileIO
{
public:
enum Flags
{
open = 0x000001,
readonly = 0x000002,
create = 0x000004
};
enum SeekMethod
{
begin = 0, current = 1, end = 2
};
enum Error
{
success = 0,
file_not_found,
sharing_violation,
unknown = -1
};
public:
FileIO();
FileIO(const char* filename, uint flg = 0);
virtual ~FileIO();
bool Open(const char* filename, uint flg = 0);
bool CreateNew(const char* filename);
bool Reopen(uint flg = 0);
void Close();
Error GetError() { return error; }
int32 Read(void* dest, int32 len);
int32 Write(const void* src, int32 len);
bool Seek(int32 fpos, SeekMethod method);
int32 Tellp();
bool SetEndOfFile();
uint GetFlags() { return flags; }
void SetLogicalOrigin(int32 origin) { lorigin = origin; }
private:
FILE* pfile;
uint flags;
uint32 lorigin;
Error error;
char path[MAX_PATH];
FileIO(const FileIO&);
const FileIO& operator=(const FileIO&);
};
#endif //

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,342 @@
// ---------------------------------------------------------------------------
// FM Sound Generator
// Copyright (C) cisc 1998, 2001.
// ---------------------------------------------------------------------------
// $Id: fmgen.h,v 1.37 2003/08/25 13:33:11 cisc Exp $
#ifndef FM_GEN_H
#define FM_GEN_H
#include "fmgen_types.h"
// ---------------------------------------------------------------------------
// 出力サンプルの型
//
// libOPNMIDI: change int32 to int16
#define FM_SAMPLETYPE int16 // int16 or int32
// ---------------------------------------------------------------------------
// 定数その1
// 静的テーブルのサイズ
#define FM_LFOBITS 8 // 変更不可
#define FM_TLBITS 7
// ---------------------------------------------------------------------------
#define FM_TLENTS (1 << FM_TLBITS)
#define FM_LFOENTS (1 << FM_LFOBITS)
#define FM_TLPOS (FM_TLENTS/4)
// サイン波の精度は 2^(1/256)
#define FM_CLENTS (0x1000 * 2) // sin + TL + LFO
// ---------------------------------------------------------------------------
namespace FM
{
// Types ----------------------------------------------------------------
typedef FM_SAMPLETYPE Sample;
typedef int32 ISample;
enum OpType { typeN=0, typeM=1 };
enum EGPhase { next, attack, decay, sustain, release, off };
void StoreSample(ISample& dest, int data);
class Chip;
struct ChipData;
// Operator -------------------------------------------------------------
struct OperatorData {
ISample out_;
ISample out2_;
ISample in2_;
uint dp_;
uint detune_;
uint detune2_;
uint multiple_;
uint32 pg_count_;
uint32 pg_diff_;
int32 pg_diff_lfo_;
OpType type_;
uint bn_;
int eg_level_;
int eg_level_on_next_phase_;
int eg_count_;
int eg_count_diff_;
int eg_out_;
int tl_out_;
int eg_rate_;
int eg_curve_count_;
#if 0 // libOPNMIDI: experimental SSG-EG
int ssg_offset_;
int ssg_vector_;
int ssg_phase_;
#endif
uint key_scale_rate_;
EGPhase eg_phase_;
uint ms_;
uint tl_;
uint tl_latch_;
uint ar_;
uint dr_;
uint sr_;
uint sl_;
uint rr_;
uint ks_;
uint ssg_type_;
bool keyon_;
bool amon_;
bool param_changed_;
bool mute_;
bool inverted_;
bool held_;
};
class Operator
{
public:
Operator();
void SetChip(Chip* chip) { chip_ = chip; }
static void MakeTimeTable(uint ratio);
ISample Calc(ISample in);
ISample CalcL(ISample in);
ISample CalcFB(uint fb);
ISample CalcFBL(uint fb);
ISample CalcN(uint noise);
void Prepare();
void KeyOn();
void KeyOff();
void Reset();
void ResetFB();
int IsOn();
void SetDT(uint dt);
void SetDT2(uint dt2);
void SetMULTI(uint multi);
void SetTL(uint tl, bool csm);
void SetKS(uint ks);
void SetAR(uint ar);
void SetDR(uint dr);
void SetSR(uint sr);
void SetRR(uint rr);
void SetSL(uint sl);
void SetSSGEC(uint ssgec);
void SetFNum(uint fnum);
void SetDPBN(uint dp, uint bn);
void SetMode(bool modulator);
void SetAMON(bool on);
void SetMS(uint ms);
void Mute(bool);
// static void SetAML(uint l);
// static void SetPML(uint l);
int Out() { return out_; }
int dbgGetIn2() { return in2_; }
void dbgStopPG() { pg_diff_ = 0; pg_diff_lfo_ = 0; }
void DataSave(struct OperatorData* data);
void DataLoad(struct OperatorData* data);
private:
typedef uint32 Counter;
Chip* chip_;
ISample out_, out2_;
ISample in2_;
// Phase Generator ------------------------------------------------------
uint32 PGCalc();
uint32 PGCalcL();
uint dp_; // ΔP
uint detune_; // Detune
uint detune2_; // DT2
uint multiple_; // Multiple
uint32 pg_count_; // Phase 現在値
uint32 pg_diff_; // Phase 差分値
int32 pg_diff_lfo_; // Phase 差分値 >> x
// Envelop Generator ---------------------------------------------------
void EGCalc();
void EGStep();
void ShiftPhase(EGPhase nextphase);
void SSGShiftPhase(int mode);
void SetEGRate(uint);
void EGUpdate();
int FBCalc(int fb);
ISample LogToLin(uint a);
OpType type_; // OP の種類 (M, N...)
uint bn_; // Block/Note
int eg_level_; // EG の出力値
int eg_level_on_next_phase_; // 次の eg_phase_ に移る値
int eg_count_; // EG の次の変移までの時間
int eg_count_diff_; // eg_count_ の差分
int eg_out_; // EG+TL を合わせた出力値
int tl_out_; // TL 分の出力値
// int pm_depth_; // PM depth
// int am_depth_; // AM depth
int eg_rate_;
int eg_curve_count_;
#if 0 // libOPNMIDI: experimental SSG-EG
int ssg_offset_;
int ssg_vector_;
int ssg_phase_;
#endif
uint key_scale_rate_; // key scale rate
EGPhase eg_phase_;
uint* ams_;
uint ms_;
uint tl_; // Total Level (0-127)
uint tl_latch_; // Total Level Latch (for CSM mode)
uint ar_; // Attack Rate (0-63)
uint dr_; // Decay Rate (0-63)
uint sr_; // Sustain Rate (0-63)
uint sl_; // Sustain Level (0-127)
uint rr_; // Release Rate (0-63)
uint ks_; // Keyscale (0-3)
uint ssg_type_; // SSG-Type Envelop Control
bool keyon_;
bool amon_; // enable Amplitude Modulation
bool param_changed_; // パラメータが更新された
bool mute_;
bool inverted_;
bool held_;
// Tables ---------------------------------------------------------------
static Counter rate_table[16];
static uint32 multable[4][16];
static const uint8 notetable[128];
static const int8 dttable[256];
static const int8 decaytable1[64][8];
static const int decaytable2[16];
static const int8 attacktable[64][8];
static const int ssgenvtable[8][2][3][2];
static uint sinetable[1024];
static int32 cltable[FM_CLENTS];
static bool tablehasmade;
static void MakeTable();
// friends --------------------------------------------------------------
friend class Channel4;
public:
int dbgopout_;
int dbgpgout_;
static const int32* dbgGetClTable() { return cltable; }
static const uint* dbgGetSineTable() { return sinetable; }
};
// 4-op Channel ---------------------------------------------------------
struct Channel4Data {
uint fb;
int buf[4];
int algo_;
struct OperatorData op[4];
};
class Channel4
{
public:
Channel4();
void SetChip(Chip* chip);
void SetType(OpType type);
ISample Calc();
ISample CalcL();
ISample CalcN(uint noise);
ISample CalcLN(uint noise);
void SetFNum(uint fnum);
void SetFB(uint fb);
void SetKCKF(uint kc, uint kf);
void SetAlgorithm(uint algo);
int Prepare();
void KeyControl(uint key);
void Reset();
void SetMS(uint ms);
void Mute(bool);
void Refresh();
void dbgStopPG() { for (int i=0; i<4; i++) op[i].dbgStopPG(); }
void DataSave(struct Channel4Data* data);
void DataLoad(struct Channel4Data* data);
private:
static const uint8 fbtable[8];
uint fb;
int buf[4];
int* in[3]; // 各 OP の入力ポインタ
int* out[3]; // 各 OP の出力ポインタ
int* pms;
int algo_;
Chip* chip_;
static void MakeTable();
static bool tablehasmade;
static int kftable[64];
public:
Operator op[4];
};
// Chip resource
struct ChipData {
uint ratio_;
uint aml_;
uint pml_;
int pmv_;
OpType optype_;
uint32 multable_[4][16];
};
class Chip
{
public:
Chip();
void SetRatio(uint ratio);
void SetAML(uint l);
void SetPML(uint l);
void SetPMV(int pmv) { pmv_ = pmv; }
uint32 GetMulValue(uint dt2, uint mul) { return multable_[dt2][mul]; }
uint GetAML() { return aml_; }
uint GetPML() { return pml_; }
int GetPMV() { return pmv_; }
uint GetRatio() { return ratio_; }
void DataSave(struct ChipData* data);
void DataLoad(struct ChipData* data);
private:
void MakeTable();
uint ratio_;
uint aml_;
uint pml_;
int pmv_;
// OpType optype_;
uint32 multable_[4][16];
};
}
#endif // FM_GEN_H

View file

@ -0,0 +1,274 @@
// ---------------------------------------------------------------------------
// FM Sound Generator
// Copyright (C) cisc 1998, 2003.
// ---------------------------------------------------------------------------
// $Id: fmgeninl.h,v 1.26 2003/06/12 13:14:36 cisc Exp $
#ifndef FM_GEN_INL_H
#define FM_GEN_INL_H
// ---------------------------------------------------------------------------
// 定数その2
//
#define FM_PI 3.14159265358979323846
#define FM_SINEPRESIS 2 // EGとサイン波の精度の差 0(低)-2(高)
#define FM_OPSINBITS 10
#define FM_OPSINENTS (1 << FM_OPSINBITS)
#define FM_EGCBITS 18 // eg の count のシフト値
#define FM_LFOCBITS 14
#ifdef FM_TUNEBUILD
#define FM_PGBITS 2
#define FM_RATIOBITS 0
#else
#define FM_PGBITS 9
#define FM_RATIOBITS 7 // 8-12 くらいまで?
#endif
#define FM_EGBITS 16
//extern int paramcount[];
//#define PARAMCHANGE(i) paramcount[i]++;
#define PARAMCHANGE(i)
namespace FM
{
// ---------------------------------------------------------------------------
// Operator
//
// フィードバックバッファをクリア
inline void Operator::ResetFB()
{
out_ = out2_ = 0;
}
// キーオン
inline void Operator::KeyOn()
{
if (!keyon_)
{
keyon_ = true;
held_ = false;
if (eg_phase_ == off || eg_phase_ == release)
{
#if 1 // libOPNMIDI: experimental SSG-EG
inverted_ = (ssg_type_ & 4) != 0;
inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN
#else
ssg_phase_ = -1;
#endif
ShiftPhase(attack);
EGUpdate();
in2_ = out_ = out2_ = 0;
pg_count_ = 0;
}
}
}
// キーオフ
inline void Operator::KeyOff()
{
if (keyon_)
{
keyon_ = false;
ShiftPhase(release);
}
}
// オペレータは稼働中か?
inline int Operator::IsOn()
{
return eg_phase_ - off;
}
// Detune (0-7)
inline void Operator::SetDT(uint dt)
{
detune_ = dt * 0x20, param_changed_ = true;
PARAMCHANGE(4);
}
// DT2 (0-3)
inline void Operator::SetDT2(uint dt2)
{
detune2_ = dt2 & 3, param_changed_ = true;
PARAMCHANGE(5);
}
// Multiple (0-15)
inline void Operator::SetMULTI(uint mul)
{
multiple_ = mul, param_changed_ = true;
PARAMCHANGE(6);
}
// Total Level (0-127) (0.75dB step)
inline void Operator::SetTL(uint tl, bool csm)
{
if (!csm)
{
tl_ = tl, param_changed_ = true;
PARAMCHANGE(7);
}
tl_latch_ = tl;
}
// Attack Rate (0-63)
inline void Operator::SetAR(uint ar)
{
ar_ = ar;
param_changed_ = true;
PARAMCHANGE(8);
}
// Decay Rate (0-63)
inline void Operator::SetDR(uint dr)
{
dr_ = dr;
param_changed_ = true;
PARAMCHANGE(9);
}
// Sustain Rate (0-63)
inline void Operator::SetSR(uint sr)
{
sr_ = sr;
param_changed_ = true;
PARAMCHANGE(10);
}
// Sustain Level (0-127)
inline void Operator::SetSL(uint sl)
{
sl_ = sl;
param_changed_ = true;
PARAMCHANGE(11);
}
// Release Rate (0-63)
inline void Operator::SetRR(uint rr)
{
rr_ = rr;
param_changed_ = true;
PARAMCHANGE(12);
}
// Keyscale (0-3)
inline void Operator::SetKS(uint ks)
{
ks_ = ks;
param_changed_ = true;
PARAMCHANGE(13);
}
// SSG-type Envelop (0-15)
inline void Operator::SetSSGEC(uint ssgec)
{
if (ssgec & 8)
ssg_type_ = ssgec;
else
ssg_type_ = 0;
}
inline void Operator::SetAMON(bool amon)
{
amon_ = amon;
param_changed_ = true;
PARAMCHANGE(14);
}
inline void Operator::Mute(bool mute)
{
mute_ = mute;
param_changed_ = true;
PARAMCHANGE(15);
}
inline void Operator::SetMS(uint ms)
{
ms_ = ms;
param_changed_ = true;
PARAMCHANGE(16);
}
// ---------------------------------------------------------------------------
// 4-op Channel
// オペレータの種類 (LFO) を設定
inline void Channel4::SetType(OpType type)
{
for (int i=0; i<4; i++)
op[i].type_ = type;
}
// セルフ・フィードバックレートの設定 (0-7)
inline void Channel4::SetFB(uint feedback)
{
fb = fbtable[feedback];
}
// OPNA 系 LFO の設定
inline void Channel4::SetMS(uint ms)
{
op[0].SetMS(ms);
op[1].SetMS(ms);
op[2].SetMS(ms);
op[3].SetMS(ms);
}
// チャンネル・マスク
inline void Channel4::Mute(bool m)
{
for (int i=0; i<4; i++)
op[i].Mute(m);
}
// 内部パラメータを再計算
inline void Channel4::Refresh()
{
for (int i=0; i<4; i++)
op[i].param_changed_ = true;
PARAMCHANGE(3);
}
inline void Channel4::SetChip(Chip* chip)
{
chip_ = chip;
for (int i=0; i<4; i++)
op[i].SetChip(chip);
}
// ---------------------------------------------------------------------------
//
//
inline void StoreSample(Sample& dest, ISample data)
{
if (sizeof(Sample) == 2)
dest = (Sample) Limit(dest + data, 0x7fff, -0x8000);
else
dest += data;
}
// ---------------------------------------------------------------------------
// AM のレベルを設定
inline void Chip::SetAML(uint l)
{
aml_ = l & (FM_LFOENTS - 1);
}
// PM のレベルを設定
inline void Chip::SetPML(uint l)
{
pml_ = l & (FM_LFOENTS - 1);
}
}
#endif // FM_GEN_INL_H

View file

@ -0,0 +1,217 @@
// ---------------------------------------------------------------------------
// FM sound generator common timer module
// Copyright (C) cisc 1998, 2000.
// ---------------------------------------------------------------------------
// $Id: fmtimer.cpp,v 1.1 2000/09/08 13:45:56 cisc Exp $
#include "fmgen_headers.h"
#include "fmgen_fmtimer.h"
using namespace FM;
// ---------------------------------------------------------------------------
// タイマー制御
//
void Timer::SetTimerControl(uint data)
{
uint tmp = regtc ^ data;
regtc = uint8(data);
if (data & 0x10)
ResetStatus(1);
if (data & 0x20)
ResetStatus(2);
if (tmp & 0x01)
timera_count = (data & 1) ? timera : 0;
if (tmp & 0x02)
timerb_count = (data & 2) ? timerb : 0;
}
#if 1
// ---------------------------------------------------------------------------
// タイマーA 周期設定
//
void Timer::SetTimerA(uint addr, uint data)
{
uint tmp;
regta[addr & 1] = uint8(data);
tmp = (regta[0] << 2) + (regta[1] & 3);
timera = (1024-tmp) * timer_step;
// LOG2("Timer A = %d %d us\n", tmp, timera >> 16);
}
// ---------------------------------------------------------------------------
// タイマーB 周期設定
//
void Timer::SetTimerB(uint data)
{
timerb = (256-data) * timer_step;
// LOG2("Timer B = %d %d us\n", data, timerb >> 12);
}
// ---------------------------------------------------------------------------
// タイマー時間処理
//
bool Timer::Count(int32 us)
{
bool event = false;
if (timera_count)
{
timera_count -= us << 16;
if (timera_count <= 0)
{
event = true;
TimerA();
while (timera_count <= 0)
timera_count += timera;
if (regtc & 4)
SetStatus(1);
}
}
if (timerb_count)
{
timerb_count -= us << 12;
if (timerb_count <= 0)
{
event = true;
while (timerb_count <= 0)
timerb_count += timerb;
if (regtc & 8)
SetStatus(2);
}
}
return event;
}
// ---------------------------------------------------------------------------
// 次にタイマーが発生するまでの時間を求める
//
int32 Timer::GetNextEvent()
{
uint32 ta = ((timera_count + 0xffff) >> 16) - 1;
uint32 tb = ((timerb_count + 0xfff) >> 12) - 1;
return (ta < tb ? ta : tb) + 1;
}
// ---------------------------------------------------------------------------
void Timer::DataSave(struct TimerData* data)
{
data->status = status;
data->regtc = regtc;
data->regta[0] = regta[0];
data->regta[1] = regta[1];
data->timera = timera;
data->timera_count = timera_count;
data->timerb = timerb;
data->timerb_count = timerb_count;
data->timer_step = timer_step;
}
// ---------------------------------------------------------------------------
void Timer::DataLoad(struct TimerData* data)
{
status = data->status;
regtc = data->regtc;
regta[0] = data->regta[0];
regta[1] = data->regta[1];
timera = data->timera;
timera_count = data->timera_count;
timerb = data->timerb;
timerb_count = data->timerb_count;
timer_step = data->timer_step;
}
// ---------------------------------------------------------------------------
// タイマー基準値設定
//
void Timer::SetTimerBase(uint clock)
{
timer_step = int32(1000000. * 65536 / clock);
}
#else
// ---------------------------------------------------------------------------
// タイマーA 周期設定
//
void Timer::SetTimerA(uint addr, uint data)
{
regta[addr & 1] = uint8(data);
timera = (1024 - ((regta[0] << 2) + (regta[1] & 3))) << 16;
}
// ---------------------------------------------------------------------------
// タイマーB 周期設定
//
void Timer::SetTimerB(uint data)
{
timerb = (256-data) << (16 + 4);
}
// ---------------------------------------------------------------------------
// タイマー時間処理
//
bool Timer::Count(int32 us)
{
bool event = false;
int tick = us * timer_step;
if (timera_count)
{
timera_count -= tick;
if (timera_count <= 0)
{
event = true;
TimerA();
while (timera_count <= 0)
timera_count += timera;
if (regtc & 4)
SetStatus(1);
}
}
if (timerb_count)
{
timerb_count -= tick;
if (timerb_count <= 0)
{
event = true;
while (timerb_count <= 0)
timerb_count += timerb;
if (regtc & 8)
SetStatus(2);
}
}
return event;
}
// ---------------------------------------------------------------------------
// 次にタイマーが発生するまでの時間を求める
//
int32 Timer::GetNextEvent()
{
uint32 ta = timera_count - 1;
uint32 tb = timerb_count - 1;
uint32 t = (ta < tb ? ta : tb) + 1;
return (t+timer_step-1) / timer_step;
}
// ---------------------------------------------------------------------------
// タイマー基準値設定
//
void Timer::SetTimerBase(uint clock)
{
timer_step = clock * 1024 / 15625;
}
#endif

View file

@ -0,0 +1,67 @@
// ---------------------------------------------------------------------------
// FM sound generator common timer module
// Copyright (C) cisc 1998, 2000.
// ---------------------------------------------------------------------------
// $Id: fmtimer.h,v 1.2 2003/04/22 13:12:53 cisc Exp $
#ifndef FM_TIMER_H
#define FM_TIMER_H
#include "fmgen_types.h"
// ---------------------------------------------------------------------------
namespace FM
{
struct TimerData {
uint8 status;
uint8 regtc;
uint8 regta[2];
int32 timera, timera_count;
int32 timerb, timerb_count;
int32 timer_step;
};
class Timer
{
public:
void Reset();
bool Count(int32 us);
int32 GetNextEvent();
void DataSave(struct TimerData* data);
void DataLoad(struct TimerData* data);
protected:
virtual void SetStatus(uint bit) = 0;
virtual void ResetStatus(uint bit) = 0;
void SetTimerBase(uint clock);
void SetTimerA(uint addr, uint data);
void SetTimerB(uint data);
void SetTimerControl(uint data);
uint8 status;
uint8 regtc;
private:
virtual void TimerA() {}
uint8 regta[2];
int32 timera, timera_count;
int32 timerb, timerb_count;
int32 timer_step;
};
// ---------------------------------------------------------------------------
// <09>‰Šú‰»
//
inline void Timer::Reset()
{
timera_count = 0;
timerb_count = 0;
}
} // namespace FM
#endif // FM_TIMER_H

View file

@ -0,0 +1,10 @@
#ifndef WIN_HEADERS_H
#define WIN_HEADERS_H
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#endif // WIN_HEADERS_H

View file

@ -0,0 +1,69 @@
// ---------------------------------------------------------------------------
// misc.h
// Copyright (C) cisc 1998, 1999.
// ---------------------------------------------------------------------------
// $Id: misc.h,v 1.5 2002/05/31 09:45:20 cisc Exp $
#ifndef MISC_H
#define MISC_H
inline int Max(int x, int y) { return (x > y) ? x : y; }
inline int Min(int x, int y) { return (x < y) ? x : y; }
inline int Abs(int x) { return x >= 0 ? x : -x; }
inline int Limit(int v, int max, int min)
{
return v > max ? max : (v < min ? min : v);
}
inline unsigned int BSwap(unsigned int a)
{
return (a >> 24) | ((a >> 8) & 0xff00) | ((a << 8) & 0xff0000) | (a << 24);
}
inline unsigned int NtoBCD(unsigned int a)
{
return ((a / 10) << 4) + (a % 10);
}
inline unsigned int BCDtoN(unsigned int v)
{
return (v >> 4) * 10 + (v & 15);
}
template<class T>
inline T gcd(T x, T y)
{
T t;
while (y)
{
t = x % y;
x = y;
y = t;
}
return x;
}
template<class T>
T bessel0(T x)
{
T p, r, s;
r = 1.0;
s = 1.0;
p = (x / 2.0) / s;
while (p > 1.0E-10)
{
r += p * p;
s += 1.0;
p *= (x / 2.0) / s;
}
return r;
}
#endif // MISC_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,523 @@
// ---------------------------------------------------------------------------
// OPN/A/B interface with ADPCM support
// Copyright (C) cisc 1998, 2003.
// ---------------------------------------------------------------------------
// $Id: opna.h,v 1.33 2003/06/12 13:14:37 cisc Exp $
#ifndef FM_OPNA_H
#define FM_OPNA_H
#include "fmgen_fmgen.h"
#include "fmgen_fmtimer.h"
#include "fmgen_psg.h"
// ---------------------------------------------------------------------------
// class OPN/OPNA
// OPN/OPNA に良く似た音を生成する音源ユニット
//
// interface:
// bool Init(uint clock, uint rate, bool, const char* path);
// 初期化.このクラスを使用する前にかならず呼んでおくこと.
// OPNA の場合はこの関数でリズムサンプルを読み込む
//
// clock: OPN/OPNA/OPNB のクロック周波数(Hz)
//
// rate: 生成する PCM の標本周波数(Hz)
//
// path: リズムサンプルのパス(OPNA のみ有効)
// 省略時はカレントディレクトリから読み込む
// 文字列の末尾には '¥' や '/' などをつけること
//
// 返り値 初期化に成功すれば true
//
// bool LoadRhythmSample(const char* path)
// (OPNA ONLY)
// Rhythm サンプルを読み直す.
// path は Init の path と同じ.
//
// bool SetRate(uint clock, uint rate, bool)
// クロックや PCM レートを変更する
// 引数等は Init を参照のこと.
//
// void Mix(FM_SAMPLETYPE* dest, int nsamples)
// Stereo PCM データを nsamples 分合成し, dest で始まる配列に
// 加える(加算する)
// ・dest には sample*2 個分の領域が必要
// ・格納形式は L, R, L, R... となる.
// ・あくまで加算なので,あらかじめ配列をゼロクリアする必要がある
// ・FM_SAMPLETYPE が short 型の場合クリッピングが行われる.
// ・この関数は音源内部のタイマーとは独立している.
// Timer は Count と GetNextEvent で操作する必要がある.
//
// void Reset()
// 音源をリセット(初期化)する
//
// void SetReg(uint reg, uint data)
// 音源のレジスタ reg に data を書き込む
//
// uint GetReg(uint reg)
// 音源のレジスタ reg の内容を読み出す
// 読み込むことが出来るレジスタは PSG, ADPCM の一部ID(0xff) とか
//
// uint ReadStatus()/ReadStatusEx()
// 音源のステータスレジスタを読み出す
// ReadStatusEx は拡張ステータスレジスタの読み出し(OPNA)
// busy フラグは常に 0
//
// bool Count(uint32 t)
// 音源のタイマーを t [μ秒] 進める.
// 音源の内部状態に変化があった時(timer オーバーフロー)
// true を返す
//
// uint32 GetNextEvent()
// 音源のタイマーのどちらかがオーバーフローするまでに必要な
// 時間[μ秒]を返す
// タイマーが停止している場合は ULONG_MAX を返す… と思う
//
// void SetVolumeFM(int db)/SetVolumePSG(int db) ...
// 各音源の音量を+−方向に調節する.標準値は 0.
// 単位は約 1/2 dB有効範囲の上限は 20 (10dB)
//
namespace FM
{
// OPN Base -------------------------------------------------------
struct OPNBaseData {
struct TimerData timer;
int fmvolume;
uint clock;
uint rate;
uint psgrate;
uint status;
uint8 prescale;
struct ChipData chip;
struct PSGData psg;
};
class OPNBase : public Timer
{
public:
OPNBase();
bool Init(uint c, uint r);
virtual void Reset();
void SetVolumeFM(int db);
void SetVolumePSG(int db);
void DataSave(struct OPNBaseData* data);
void DataLoad(struct OPNBaseData* data);
protected:
void SetParameter(Channel4* ch, uint addr, uint data);
void SetPrescaler(uint p);
void RebuildTimeTable();
int fmvolume;
uint clock; // OPN クロック
uint rate; // FM 音源合成レート
uint psgrate; // FMGen 出力レート
uint status;
Channel4* csmch;
static uint32 lfotable[8];
private:
void TimerA();
uint8 prescale;
protected:
Chip chip;
PSG psg;
};
// OPN2 Base ------------------------------------------------------
struct OPNABaseData {
struct OPNBaseData opnbase;
uint8 pan[6];
uint16 panvolume_l[6]; // libOPNMIDI: soft panning
uint16 panvolume_r[6]; // libOPNMIDI: soft panning
uint8 fnum2[9];
uint8 reg22;
uint reg29;
uint stmask;
uint statusnext;
uint32 lfocount;
uint32 lfodcount;
uint fnum[6];
uint fnum3[3];
bool is_adpcmbuf;
uint8 adpcmbuf[0x40000];
uint adpcmmask;
uint adpcmnotice;
uint startaddr;
uint stopaddr;
uint memaddr;
uint limitaddr;
int adpcmlevel;
int adpcmvolume;
int adpcmvol;
uint deltan;
int adplc;
int adpld;
uint adplbase;
int adpcmx;
int adpcmd;
int adpcmout;
int apout0;
int apout1;
uint adpcmreadbuf;
bool adpcmplay;
int8 granuality;
bool adpcmmask_;
uint8 control1;
uint8 control2;
uint8 adpcmreg[8];
int rhythmmask_;
struct Channel4Data ch[6];
};
class OPNABase : public OPNBase
{
public:
OPNABase();
~OPNABase();
uint ReadStatus() { return status & 0x03; }
uint ReadStatusEx();
void SetChannelMask(uint mask);
// libOPNMIDI: soft panning
void SetPan(uint c, uint8 p);
void DataSave(struct OPNABaseData* data);
void DataLoad(struct OPNABaseData* data);
private:
virtual void Intr(bool) {}
void MakeTable2();
protected:
bool Init(uint c, uint r, bool);
bool SetRate(uint c, uint r, bool);
void Reset();
void SetReg(uint addr, uint data);
void SetADPCMBReg(uint reg, uint data);
uint GetReg(uint addr);
protected:
void FMMix(Sample* buffer, int nsamples);
void Mix6(Sample* buffer, int nsamples, int activech);
void MixSubS(int activech, ISample**);
void MixSubSL(int activech, ISample**);
void SetStatus(uint bit);
void ResetStatus(uint bit);
void UpdateStatus();
void LFO();
void DecodeADPCMB();
void ADPCMBMix(Sample* dest, uint count);
void WriteRAM(uint data);
uint ReadRAM();
int ReadRAMN();
int DecodeADPCMBSample(uint);
// FM 音源関係
uint8 pan[6];
uint16 panvolume_l[6]; // libOPNMIDI: soft panning
uint16 panvolume_r[6]; // libOPNMIDI: soft panning
uint8 fnum2[9];
uint8 reg22;
uint reg29; // OPNA only?
uint stmask;
uint statusnext;
uint32 lfocount;
uint32 lfodcount;
uint fnum[6];
uint fnum3[3];
// ADPCM 関係
uint8* adpcmbuf; // ADPCM RAM
uint adpcmmask; // メモリアドレスに対するビットマスク
uint adpcmnotice; // ADPCM 再生終了時にたつビット
uint startaddr; // Start address
uint stopaddr; // Stop address
uint memaddr; // 再生中アドレス
uint limitaddr; // Limit address/mask
int adpcmlevel; // ADPCM 音量
int adpcmvolume;
int adpcmvol;
uint deltan; // N
int adplc; // 周波数変換用変数
int adpld; // 周波数変換用変数差分値
uint adplbase; // adpld の元
int adpcmx; // ADPCM 合成用 x
int adpcmd; // ADPCM 合成用
int adpcmout; // ADPCM 合成後の出力
int apout0; // out(t-2)+out(t-1)
int apout1; // out(t-1)+out(t)
uint adpcmreadbuf; // ADPCM リード用バッファ
bool adpcmplay; // ADPCM 再生中
int8 granuality;
bool adpcmmask_;
uint8 control1; // ADPCM コントロールレジスタ1
uint8 control2; // ADPCM コントロールレジスタ2
uint8 adpcmreg[8]; // ADPCM レジスタの一部分
int rhythmmask_;
Channel4 ch[6];
static void BuildLFOTable();
static int amtable[FM_LFOENTS];
static int pmtable[FM_LFOENTS];
static int32 tltable[FM_TLENTS+FM_TLPOS];
static bool tablehasmade;
};
// YM2203(OPN) ----------------------------------------------------
struct OPNData {
struct OPNBaseData opnbase;
uint fnum[3];
uint fnum3[3];
uint8 fnum2[6];
struct Channel4Data ch[3];
};
class OPN : public OPNBase
{
public:
OPN();
virtual ~OPN() {}
bool Init(uint c, uint r, bool=false, const char* =0);
bool SetRate(uint c, uint r, bool=false);
void Reset();
void Mix(Sample* buffer, int nsamples);
void SetReg(uint addr, uint data);
uint GetReg(uint addr);
uint ReadStatus() { return status & 0x03; }
uint ReadStatusEx() { return 0xff; }
void SetChannelMask(uint mask);
int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; }
int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; }
Channel4* dbgGetCh(int c) { return &ch[c]; }
void DataSave(struct OPNData* data);
void DataLoad(struct OPNData* data);
private:
virtual void Intr(bool) {}
void SetStatus(uint bit);
void ResetStatus(uint bit);
uint fnum[3];
uint fnum3[3];
uint8 fnum2[6];
Channel4 ch[3];
};
// YM2608(OPNA) ---------------------------------------------------
struct Rhythm
{
uint8 pan; // ぱん
int8 level; // おんりょう
int volume; // おんりょうせってい
int16* sample; // さんぷる
uint size; // さいず
uint pos; // いち
uint step; // すてっぷち
uint rate; // さんぷるのれーと
};
struct OPNAData {
struct OPNABaseData opnabase;
Rhythm rhythm[6];
int8 rhythmtl;
int rhythmtvol;
uint8 rhythmkey;
};
class OPNA : public OPNABase
{
public:
OPNA();
virtual ~OPNA();
bool Init(uint c, uint r, bool = false, const char* rhythmpath=0);
bool LoadRhythmSample(const char*);
bool SetRate(uint c, uint r, bool = false);
void Mix(Sample* buffer, int nsamples);
void Reset();
void SetReg(uint addr, uint data);
uint GetReg(uint addr);
void SetVolumeADPCM(int db);
void SetVolumeRhythmTotal(int db);
void SetVolumeRhythm(int index, int db);
uint8* GetADPCMBuffer() { return adpcmbuf; }
int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; }
int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; }
Channel4* dbgGetCh(int c) { return &ch[c]; }
void DataSave(struct OPNAData* data);
void DataLoad(struct OPNAData* data);
private:
void RhythmMix(Sample* buffer, uint count);
// リズム音源関係
Rhythm rhythm[6];
int8 rhythmtl; // リズム全体の音量
int rhythmtvol;
uint8 rhythmkey; // リズムのキー
};
// YM2610/B(OPNB) ---------------------------------------------------
struct ADPCMA
{
uint8 pan; // ぱん
int8 level; // おんりょう
int volume; // おんりょうせってい
uint pos; // いち
uint step; // すてっぷち
uint start; // 開始
uint stop; // 終了
uint nibble; // 次の 4 bit
int adpcmx; // 変換用
int adpcmd; // 変換用
};
struct OPNBData {
struct OPNABaseData opnabase;
// uint8* adpcmabuf;
int adpcmasize;
ADPCMA adpcma[6];
int8 adpcmatl;
int adpcmatvol;
uint8 adpcmakey;
int adpcmastep;
uint8 adpcmareg[32];
struct Channel4Data ch[6];
};
class OPNB : public OPNABase
{
public:
OPNB();
virtual ~OPNB();
bool Init(uint c, uint r, bool = false,
uint8 *_adpcma = 0, int _adpcma_size = 0,
uint8 *_adpcmb = 0, int _adpcmb_size = 0);
bool SetRate(uint c, uint r, bool = false);
void Mix(Sample* buffer, int nsamples);
void Reset();
void SetReg(uint addr, uint data);
uint GetReg(uint addr);
uint ReadStatusEx();
void SetVolumeADPCMATotal(int db);
void SetVolumeADPCMA(int index, int db);
void SetVolumeADPCMB(int db);
// void SetChannelMask(uint mask);
void DataSave(struct OPNBData* data, void* adpcmdata);
void DataLoad(struct OPNBData* data, void* adpcmdata);
private:
int DecodeADPCMASample(uint);
void ADPCMAMix(Sample* buffer, uint count);
static void InitADPCMATable();
// ADPCMA 関係
uint8* adpcmabuf; // ADPCMA ROM
int adpcmasize;
ADPCMA adpcma[6];
int8 adpcmatl; // ADPCMA 全体の音量
int adpcmatvol;
uint8 adpcmakey; // ADPCMA のキー
int adpcmastep;
uint8 adpcmareg[32];
static int jedi_table[(48+1)*16];
Channel4 ch[6];
};
// YM2612/3438(OPN2) ----------------------------------------------------
class OPN2 : public OPNBase
{
public:
OPN2();
virtual ~OPN2() {}
bool Init(uint c, uint r, bool=false, const char* =0);
bool SetRate(uint c, uint r, bool);
void Reset();
void Mix(Sample* buffer, int nsamples);
void SetReg(uint addr, uint data);
uint GetReg(uint addr);
uint ReadStatus() { return status & 0x03; }
uint ReadStatusEx() { return 0xff; }
void SetChannelMask(uint mask);
private:
virtual void Intr(bool) {}
void SetStatus(uint bit);
void ResetStatus(uint bit);
uint fnum[3];
uint fnum3[3];
uint8 fnum2[6];
// 線形補間用ワーク
int32 mixc, mixc1;
Channel4 ch[3];
};
}
// ---------------------------------------------------------------------------
inline void FM::OPNBase::RebuildTimeTable()
{
int p = prescale;
prescale = -1;
SetPrescaler(p);
}
inline void FM::OPNBase::SetVolumePSG(int db)
{
psg.SetVolume(db);
}
#endif // FM_OPNA_H

View file

@ -0,0 +1,395 @@
// ---------------------------------------------------------------------------
// PSG Sound Implementation
// Copyright (C) cisc 1997, 1999.
// ---------------------------------------------------------------------------
// $Id: psg.cpp,v 1.10 2002/05/15 21:38:01 cisc Exp $
#include "fmgen_headers.h"
#include "fmgen_misc.h"
#include "fmgen_psg.h"
// ---------------------------------------------------------------------------
// コンストラクタ・デストラクタ
//
PSG::PSG()
{
SetVolume(0);
MakeNoiseTable();
Reset();
mask = 0x3f;
}
PSG::~PSG()
{
}
// ---------------------------------------------------------------------------
// PSG を初期化する(RESET)
//
void PSG::Reset()
{
for (int i=0; i<14; i++)
SetReg(i, 0);
SetReg(7, 0xff);
SetReg(14, 0xff);
SetReg(15, 0xff);
}
// ---------------------------------------------------------------------------
// クロック周波数の設定
//
void PSG::SetClock(int clock, int rate)
{
tperiodbase = int((1 << toneshift ) / 4.0 * clock / rate);
eperiodbase = int((1 << envshift ) / 4.0 * clock / rate);
nperiodbase = int((1 << noiseshift) / 4.0 * clock / rate);
// 各データの更新
int tmp;
tmp = ((reg[0] + reg[1] * 256) & 0xfff);
speriod[0] = tmp ? tperiodbase / tmp : tperiodbase;
tmp = ((reg[2] + reg[3] * 256) & 0xfff);
speriod[1] = tmp ? tperiodbase / tmp : tperiodbase;
tmp = ((reg[4] + reg[5] * 256) & 0xfff);
speriod[2] = tmp ? tperiodbase / tmp : tperiodbase;
tmp = reg[6] & 0x1f;
nperiod = tmp ? nperiodbase / tmp / 2 : nperiodbase / 2;
tmp = ((reg[11] + reg[12] * 256) & 0xffff);
eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2;
}
// ---------------------------------------------------------------------------
// ノイズテーブルを作成する
//
void PSG::MakeNoiseTable()
{
if (!noisetable[0])
{
int noise = 14321;
for (int i=0; i<noisetablesize; i++)
{
int n = 0;
for (int j=0; j<32; j++)
{
n = n * 2 + (noise & 1);
noise = (noise >> 1) | (((noise << 14) ^ (noise << 16)) & 0x10000);
}
noisetable[i] = n;
}
}
}
// ---------------------------------------------------------------------------
// 出力テーブルを作成
// 素直にテーブルで持ったほうが省スペース。
//
void PSG::SetVolume(int volume)
{
double base = 0x4000 / 3.0 * pow(10.0, volume / 40.0);
for (int i=31; i>=2; i--)
{
EmitTable[i] = int(base);
base /= 1.189207115;
}
EmitTable[1] = 0;
EmitTable[0] = 0;
MakeEnvelopTable();
SetChannelMask(~mask);
}
void PSG::SetChannelMask(int c)
{
mask = ~c;
for (int i=0; i<3; i++)
olevel[i] = mask & (1 << i) ? EmitTable[(reg[8+i] & 15) * 2 + 1] : 0;
}
// ---------------------------------------------------------------------------
// エンベロープ波形テーブル
//
void PSG::MakeEnvelopTable()
{
// 0 lo 1 up 2 down 3 hi
static uint8 table1[16*2] =
{
2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0,
2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0,
};
static uint8 table2[4] = { 0, 0, 31, 31 };
static int8 table3[4] = { 0, 1, -1, 0 };
uint* ptr = enveloptable[0];
for (int i=0; i<16*2; i++)
{
uint8 v = table2[table1[i]];
for (int j=0; j<32; j++)
{
*ptr++ = EmitTable[v];
v += table3[table1[i]];
}
}
}
// ---------------------------------------------------------------------------
// PSG のレジスタに値をセットする
// regnum レジスタの番号 (0 - 15)
// data セットする値
//
void PSG::SetReg(uint regnum, uint8 data)
{
if (regnum < 0x10)
{
reg[regnum] = data;
switch (regnum)
{
int tmp;
case 0: // ChA Fine Tune
case 1: // ChA Coarse Tune
tmp = ((reg[0] + reg[1] * 256) & 0xfff);
speriod[0] = tmp ? tperiodbase / tmp : tperiodbase;
break;
case 2: // ChB Fine Tune
case 3: // ChB Coarse Tune
tmp = ((reg[2] + reg[3] * 256) & 0xfff);
speriod[1] = tmp ? tperiodbase / tmp : tperiodbase;
break;
case 4: // ChC Fine Tune
case 5: // ChC Coarse Tune
tmp = ((reg[4] + reg[5] * 256) & 0xfff);
speriod[2] = tmp ? tperiodbase / tmp : tperiodbase;
break;
case 6: // Noise generator control
data &= 0x1f;
nperiod = data ? nperiodbase / data : nperiodbase;
break;
case 8:
olevel[0] = mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 9:
olevel[1] = mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 10:
olevel[2] = mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 11: // Envelop period
case 12:
tmp = ((reg[11] + reg[12] * 256) & 0xffff);
eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2;
break;
case 13: // Envelop shape
ecount = 0;
envelop = enveloptable[data & 15];
break;
}
}
}
// ---------------------------------------------------------------------------
void PSG::DataSave(struct PSGData* data) {
memcpy(data->reg, reg, 16);
memcpy(data->olevel, olevel, sizeof(uint) * 6);
memcpy(data->scount, scount, sizeof(uint32) * 3);
memcpy(data->speriod, speriod, sizeof(uint32) * 3);
data->ecount = ecount;
data->eperiod = eperiod;
data->ncount = ncount;
data->nperiod = nperiod;
data->tperiodbase = tperiodbase;
data->eperiodbase = eperiodbase;
data->nperiodbase = nperiodbase;
data->volume = volume;
data->mask = mask;
}
// ---------------------------------------------------------------------------
void PSG::DataLoad(struct PSGData* data) {
memcpy(reg, data->reg, 16);
memcpy(olevel, data->olevel, sizeof(uint) * 6);
memcpy(scount, data->scount, sizeof(uint32) * 3);
memcpy(speriod, data->speriod, sizeof(uint32) * 3);
ecount = data->ecount;
eperiod = data->eperiod;
ncount = data->ncount;
nperiod = data->nperiod;
tperiodbase = data->tperiodbase;
eperiodbase = data->eperiodbase;
nperiodbase = data->nperiodbase;
volume = data->volume;
mask = data->mask;
}
// ---------------------------------------------------------------------------
//
//
inline void PSG::StoreSample(Sample& dest, int32 data)
{
if (sizeof(Sample) == 2)
dest = (Sample) Limit(dest + data, 0x7fff, -0x8000);
else
dest += data;
}
// ---------------------------------------------------------------------------
// PCM データを吐き出す(2ch)
// dest PCM データを展開するポインタ
// nsamples 展開する PCM のサンプル数
//
void PSG::Mix(Sample* dest, int nsamples)
{
uint8 chenable[3], nenable[3];
uint8 r7 = ~reg[7];
if ((r7 & 0x3f) | ((reg[8] | reg[9] | reg[10]) & 0x1f))
{
chenable[0] = (r7 & 0x01) && (speriod[0] <= (1 << toneshift));
chenable[1] = (r7 & 0x02) && (speriod[1] <= (1 << toneshift));
chenable[2] = (r7 & 0x04) && (speriod[2] <= (1 << toneshift));
nenable[0] = (r7 >> 3) & 1;
nenable[1] = (r7 >> 4) & 1;
nenable[2] = (r7 >> 5) & 1;
int noise, sample;
uint env;
uint* p1 = ((mask & 1) && (reg[ 8] & 0x10)) ? &env : &olevel[0];
uint* p2 = ((mask & 2) && (reg[ 9] & 0x10)) ? &env : &olevel[1];
uint* p3 = ((mask & 4) && (reg[10] & 0x10)) ? &env : &olevel[2];
#define SCOUNT(ch) (scount[ch] >> (toneshift+oversampling))
if (p1 != &env && p2 != &env && p3 != &env)
{
// エンベロープ無し
if ((r7 & 0x38) == 0)
{
// ノイズ無し
for (int i=0; i<nsamples; i++)
{
sample = 0;
for (int j=0; j < (1 << oversampling); j++)
{
int x, y, z;
x = (SCOUNT(0) & chenable[0]) - 1;
sample += (olevel[0] + x) ^ x;
scount[0] += speriod[0];
y = (SCOUNT(1) & chenable[1]) - 1;
sample += (olevel[1] + y) ^ y;
scount[1] += speriod[1];
z = (SCOUNT(2) & chenable[2]) - 1;
sample += (olevel[2] + z) ^ z;
scount[2] += speriod[2];
}
sample /= (1 << oversampling);
StoreSample(dest[0], sample);
StoreSample(dest[1], sample);
dest += 2;
}
}
else
{
// ノイズ有り
for (int i=0; i<nsamples; i++)
{
sample = 0;
for (int j=0; j < (1 << oversampling); j++)
{
#ifdef _M_IX86
noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)]
>> (ncount >> (noiseshift+oversampling+1));
#else
noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)]
>> (ncount >> (noiseshift+oversampling+1) & 31);
#endif
ncount += nperiod;
int x, y, z;
x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1
sample += (olevel[0] + x) ^ x;
scount[0] += speriod[0];
y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1;
sample += (olevel[1] + y) ^ y;
scount[1] += speriod[1];
z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1;
sample += (olevel[2] + z) ^ z;
scount[2] += speriod[2];
}
sample /= (1 << oversampling);
StoreSample(dest[0], sample);
StoreSample(dest[1], sample);
dest += 2;
}
}
// エンベロープの計算をさぼった帳尻あわせ
ecount = (ecount >> 8) + (eperiod >> (8-oversampling)) * nsamples;
if (ecount >= (1 << (envshift+6+oversampling-8)))
{
if ((reg[0x0d] & 0x0b) != 0x0a)
ecount |= (1 << (envshift+5+oversampling-8));
ecount &= (1 << (envshift+6+oversampling-8)) - 1;
}
ecount <<= 8;
}
else
{
// エンベロープあり
for (int i=0; i<nsamples; i++)
{
sample = 0;
for (int j=0; j < (1 << oversampling); j++)
{
env = envelop[ecount >> (envshift+oversampling)];
ecount += eperiod;
if (ecount >= (1 << (envshift+6+oversampling)))
{
if ((reg[0x0d] & 0x0b) != 0x0a)
ecount |= (1 << (envshift+5+oversampling));
ecount &= (1 << (envshift+6+oversampling)) - 1;
}
#ifdef _M_IX86
noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)]
>> (ncount >> (noiseshift+oversampling+1));
#else
noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)]
>> (ncount >> (noiseshift+oversampling+1) & 31);
#endif
ncount += nperiod;
int x, y, z;
x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1
sample += (*p1 + x) ^ x;
scount[0] += speriod[0];
y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1;
sample += (*p2 + y) ^ y;
scount[1] += speriod[1];
z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1;
sample += (*p3 + z) ^ z;
scount[2] += speriod[2];
}
sample /= (1 << oversampling);
StoreSample(dest[0], sample);
StoreSample(dest[1], sample);
dest += 2;
}
}
}
}
// ---------------------------------------------------------------------------
// テーブル
//
uint PSG::noisetable[noisetablesize] = { 0, };
int PSG::EmitTable[0x20] = { -1, };
uint PSG::enveloptable[16][64] = { {0, } };

115
thirdparty/opnmidi/chips/np2/fmgen_psg.h vendored Normal file
View file

@ -0,0 +1,115 @@
// ---------------------------------------------------------------------------
// PSG-like sound generator
// Copyright (C) cisc 1997, 1999.
// ---------------------------------------------------------------------------
// $Id: psg.h,v 1.8 2003/04/22 13:12:53 cisc Exp $
#ifndef PSG_H
#define PSG_H
#include "fmgen_types.h"
// libOPNMIDI: change int32 to int16
#define PSG_SAMPLETYPE int16 // int32 or int16
// ---------------------------------------------------------------------------
// class PSG
// PSG に良く似た音を生成する音源ユニット
//
// interface:
// bool SetClock(uint clock, uint rate)
// 初期化.このクラスを使用する前にかならず呼んでおくこと.
// PSG のクロックや PCM レートを設定する
//
// clock: PSG の動作クロック
// rate: 生成する PCM のレート
// retval 初期化に成功すれば true
//
// void Mix(Sample* dest, int nsamples)
// PCM を nsamples 分合成し, dest で始まる配列に加える(加算する)
// あくまで加算なので,最初に配列をゼロクリアする必要がある
//
// void Reset()
// リセットする
//
// void SetReg(uint reg, uint8 data)
// レジスタ reg に data を書き込む
//
// uint GetReg(uint reg)
// レジスタ reg の内容を読み出す
//
// void SetVolume(int db)
// 各音源の音量を調節する
// 単位は約 1/2 dB
//
struct PSGData {
uint8 reg[16];
uint olevel[3];
uint32 scount[3];
uint32 speriod[3];
uint32 ecount;
uint32 eperiod;
uint32 ncount;
uint32 nperiod;
uint32 tperiodbase;
uint32 eperiodbase;
uint32 nperiodbase;
int volume;
int mask;
};
class PSG
{
public:
typedef PSG_SAMPLETYPE Sample;
enum
{
noisetablesize = 1 << 11, // ←メモリ使用量を減らしたいなら減らして
toneshift = 24,
envshift = 22,
noiseshift = 14,
oversampling = 2 // ← 音質より速度が優先なら減らすといいかも
};
public:
PSG();
~PSG();
void Mix(Sample* dest, int nsamples);
void SetClock(int clock, int rate);
void SetVolume(int vol);
void SetChannelMask(int c);
void Reset();
void SetReg(uint regnum, uint8 data);
uint GetReg(uint regnum) { return reg[regnum & 0x0f]; }
void DataSave(struct PSGData* data);
void DataLoad(struct PSGData* data);
protected:
void MakeNoiseTable();
void MakeEnvelopTable();
static void StoreSample(Sample& dest, int32 data);
uint8 reg[16];
const uint* envelop;
uint olevel[3];
uint32 scount[3], speriod[3];
uint32 ecount, eperiod;
uint32 ncount, nperiod;
uint32 tperiodbase;
uint32 eperiodbase;
uint32 nperiodbase;
int volume;
int mask;
static uint enveloptable[16][64];
static uint noisetable[noisetablesize];
static int EmitTable[32];
};
#endif // PSG_H

View file

@ -0,0 +1,93 @@
------------------------------------------------------------------------------
FM Sound Generator with OPN/OPM interface
Copyright (C) by cisc 1998, 2003.
------------------------------------------------------------------------------
【概要】
C++ による FM/PSG 音源の実装です。
AY8910, YM2203, YM2151, YM2608, YM2610 相当のインターフェースも
実装してあります。
【使い方】
(TODO:中身を書く)
【注意】
以前のバージョンから幾分手を加えました。インターフェースは
いじっていないつもりですが、何かしらのバグが隠れているかもしれません。
YM2610 は動作チェックすらしていません。
線形補完モード(interpolation)は廃止されました。
Init() の引数仕様は変わっていませんが、interpolation = true にしても
動作は変わりません。
OPNA::Init/SetRate で与えるチップクロックの値の仕様が
以前の M88 に搭載されていたバージョンと異なっています.
【著作権、免責規定】
・本ソースコードは作者(cisc@retropc.net) が著作権を所有しています。
・本ソースコードはあるがままに提供されるものであり,
暗黙及び明示的な保証を一切含みません.
・本ソースコードを利用したこと,利用しなかったこと,
利用できなかったことに関して生じたあるいは生じると予測される
損害について,作者は一切責任を負いません.
・本ソースコードは,以下の制限を満たす限り自由に改変・組み込み・
配布・利用することができます.
1. 本ソフトの由来(作者, 著作権)を明記すること.
2. 配布する際にはフリーソフトとすること.
3. 改変したソースコードを配布する際は改変内容を明示すること.
4. ソースコードを配布する際にはこのテキストを一切改変せずに
そのまま添付すること.
・公開の際に作者への連絡を頂ければ幸いです.
・商用ソフト(シェアウェア含む) に本ソースコードの一部,または
全部を組み込む際には,事前に作者の合意を得る必要があります.
【変更点】
008 030902 ・出力サンプリングレートの設定を、チップ元来の合成サンプ
リングレートと異なる設定にするとエンベロープがおかしく
なる問題を修正。
007a 030608 ・領域外アクセスがあったのを修正 (Thanks to PI.様)
007 030607 ・再現性の向上
・OPN: SSG-EG のサポート
・線形補完の廃止
・asm 版の廃止
・マルチスレッドフリーになった?
006 010330 ・再現性に関していくらか手直し(正弦波,出力タイミング等)
005 010317 ・OPN: FM 音源の合成周波数が出力周波数よりも低いときに,
補完を使うと音痴になる/音がまともに出なくなる問題を修正.
・FM: 補完を使わない時の精度を上げてみる.
問題が起きたら fmgeninl.h の FM_RATIOBITS を 8 に戻すと吉.
004a 010311 ・OPM: ノイズをそこそこ聞けるレベル()まで修正.
・OPNA/OPM: FM_USE_CALC2 廃止.
・デグレ修正, なんのために cvs で管理しているんだか…(T-T
OPNB: ADPCMB ROM マスク作成ミス.
OPNB: ADPCMA ステータスフラグ関係の挙動修復.
OPM: LFO パラメータの初期化を忘れていたのを修正.
003 010124 ・OPNA/OPM 実際には補完前の値を出力していたバグを修正.
002 010123 ・合成周波数が出力周波数より低いときでも補間が効くようにする.
・OPN: 補間時にプリスケーラの設定を変更したときに音化けする
バグを修正.
・OPNA/B: LFO が効かないバグを修正.
001 000928 ・初期バージョン
$Id: readme.txt,v 1.1 2003/06/07 08:25:20 cisc Exp $

View file

@ -0,0 +1,24 @@
NP2kai において fmgen 008 へ行った改変. by AZO
1.ファイル名の変更. fmgen_*.*
fmgen.cpp Operator::MakeTable() のインクリメント修正.
[修正前]
*p++ = p[-512] / 2;
[修正後]
//*p++ = p[-512] / 2;
*p = p[-512] / 2;
p++;
3.各クラスのステートセーブ用構造体と、ステートセーブ/ロード実装
struct xxxData : ステートセーブ用構造体
xxx_DataSave() : ステートセーブ
xxx_DataLoad() : ステートロード
C言語用ラッパー fmgen_fmgwrap.cpp/.h
以上.

View file

@ -0,0 +1,23 @@
#if !defined(win32_types_h)
#define win32_types_h
#include "compiler.h"
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef signed char sint8;
typedef signed short sint16;
typedef signed int sint32;
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
#endif // win32_types_h

95
thirdparty/opnmidi/chips/np2_opna.cpp vendored Normal file
View file

@ -0,0 +1,95 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "np2_opna.h"
#include "np2/fmgen_opna.h"
#include <new>
#include <cstdio>
#include <cstdlib>
#include <cstring>
template <class ChipType>
NP2OPNA<ChipType>::NP2OPNA(OPNFamily f)
: ChipBase(f)
{
ChipType *opn = (ChipType *)std::calloc(1, sizeof(ChipType));
chip = new(opn) ChipType;
opn->Init(ChipBase::m_clock, ChipBase::m_rate);
opn->SetReg(0x29, 0x9f); // enable channels 4-6
}
template <class ChipType>
NP2OPNA<ChipType>::~NP2OPNA()
{
chip->~ChipType();
std::free(chip);
}
template <class ChipType>
void NP2OPNA<ChipType>::setRate(uint32_t rate, uint32_t clock)
{
ChipBase::setRate(rate, clock);
uint32_t chipRate = ChipBase::isRunningAtPcmRate() ? rate : ChipBase::nativeRate();
chip->SetRate(clock, chipRate, false); // implies Reset()
chip->SetReg(0x29, 0x9f); // enable channels 4-6
}
template <class ChipType>
void NP2OPNA<ChipType>::reset()
{
ChipBase::reset();
chip->Reset();
chip->SetReg(0x29, 0x9f); // enable channels 4-6
}
template <class ChipType>
void NP2OPNA<ChipType>::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
chip->SetReg((port << 8) | addr, data);
}
template <class ChipType>
void NP2OPNA<ChipType>::writePan(uint16_t chan, uint8_t data)
{
chip->SetPan(chan, data);
}
template <class ChipType>
void NP2OPNA<ChipType>::nativeGenerateN(int16_t *output, size_t frames)
{
std::memset(output, 0, 2 * frames * sizeof(output[0]));
chip->Mix(output, static_cast<int>(frames));
}
template <>
const char *NP2OPNA<FM::OPNA>::emulatorName()
{
return "Neko Project II Kai OPNA"; // git 2018-10-28 rev e1c0609
}
template <>
const char *NP2OPNA<FM::OPNB>::emulatorName()
{
return "Neko Project II Kai OPNB"; // git 2018-10-28 rev e1c0609
}
// template class NP2OPNA<FM::OPN2>;
template class NP2OPNA<FM::OPNA>;
template class NP2OPNA<FM::OPNB>;

48
thirdparty/opnmidi/chips/np2_opna.h vendored Normal file
View file

@ -0,0 +1,48 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NP2_OPNA_H
#define NP2_OPNA_H
#include "opn_chip_base.h"
namespace FM { class OPN2; class OPNA; class OPNB; }
template <class ChipType = FM::OPNA>
class NP2OPNA final : public OPNChipBaseBufferedT<NP2OPNA<ChipType > >
{
typedef OPNChipBaseBufferedT<NP2OPNA<ChipType > > ChipBase;
ChipType *chip;
public:
explicit NP2OPNA(OPNFamily f);
~NP2OPNA() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void writePan(uint16_t chan, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerateN(int16_t *output, size_t frames) override;
const char *emulatorName() override;
enum { resamplerPostAttenuate = 2 };
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Alexey Khokholov (Nuke.YKT)
* Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -23,7 +23,7 @@
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
* OPL2 ROMs.
*
* version: 1.0.7
* version: 1.0.9
*/
#include <string.h>
@ -242,7 +242,7 @@ static const Bit16u panlawtable[] =
4858, 4050, 3240, 2431, 1620, 810, 0
};
static Bit32u chip_type = ym3438_type_discrete;
static Bit32u chip_type = ym3438_mode_readmode;
void OPN2_DoIO(ym3438_t *chip)
{
@ -388,7 +388,7 @@ void OPN2_DoRegWrite(ym3438_t *chip)
/* Data */
if (chip->write_d_en && (chip->write_data & 0x100) == 0)
{
switch (chip->address)
switch (chip->write_fm_mode_a)
{
case 0x21: /* LSI test 1 */
for (i = 0; i < 8; i++)
@ -467,7 +467,7 @@ void OPN2_DoRegWrite(ym3438_t *chip)
/* Address */
if (chip->write_a_en)
{
chip->write_fm_mode_a = chip->write_data & 0xff;
chip->write_fm_mode_a = chip->write_data & 0x1ff;
}
}
@ -1007,7 +1007,7 @@ void OPN2_ChOutput(ym3438_t *chip)
chip->mol = 0;
chip->mor = 0;
if (chip_type == ym3438_type_ym2612)
if (chip_type & ym3438_mode_ym2612)
{
out_en = ((cycles & 3) == 3) || test_dac;
/* YM2612 DAC emulation(not verified) */
@ -1040,11 +1040,6 @@ void OPN2_ChOutput(ym3438_t *chip)
else
{
out_en = ((cycles & 3) != 0) || test_dac;
/* Discrete YM3438 seems has the ladder effect too */
if (out >= 0 && chip_type == ym3438_type_discrete)
{
out++;
}
if (chip->ch_lock_l && out_en)
{
chip->mol = out;
@ -1381,6 +1376,9 @@ void OPN2_Clock(ym3438_t *chip, Bit16s *buffer)
buffer[0] = chip->mol;
buffer[1] = chip->mor;
if (chip->status_time)
chip->status_time--;
}
void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data)
@ -1420,7 +1418,7 @@ Bit32u OPN2_ReadIRQPin(ym3438_t *chip)
Bit8u OPN2_Read(ym3438_t *chip, Bit32u port)
{
if ((port & 3) == 0 || chip_type == ym3438_type_asic)
if ((port & 3) == 0 || (chip_type & ym3438_mode_readmode))
{
if (chip->mode_test_21[6])
{
@ -1438,23 +1436,34 @@ Bit8u OPN2_Read(ym3438_t *chip, Bit32u port)
}
if (chip->mode_test_21[7])
{
return testdata & 0xff;
chip->status = testdata & 0xff;
}
else
{
return testdata >> 8;
chip->status = testdata >> 8;
}
}
else
{
return (Bit8u)(chip->busy << 7) | (Bit8u)(chip->timer_b_overflow_flag << 1)
| (Bit8u)chip->timer_a_overflow_flag;
chip->status = (chip->busy << 7) | (chip->timer_b_overflow_flag << 1)
| chip->timer_a_overflow_flag;
}
if (chip_type & ym3438_mode_ym2612)
{
chip->status_time = 300000;
}
else
{
chip->status_time = 40000000;
}
}
if (chip->status_time)
{
return chip->status;
}
return 0;
}
void OPN2_WritePan(ym3438_t *chip, Bit32u channel, Bit8u data)
{
chip->pan_volume_l[channel] = panlawtable[data & 0x7F];
@ -1611,24 +1620,6 @@ void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples)
}
}
void OPN2_SetOptions(Bit8u flags)
{
switch ((flags >> 3) & 0x03)
{
case 0x00: /* YM2612 */
default:
OPN2_SetChipType(ym3438_type_ym2612);
break;
case 0x01: /* ASIC YM3438 */
OPN2_SetChipType(ym3438_type_asic);
break;
case 0x02: /* Discrete YM3438 */
OPN2_SetChipType(ym3438_type_discrete);
break;
}
}
void OPN2_SetMute(ym3438_t *chip, Bit32u mute)
{
Bit32u i;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 Alexey Khokholov (Nuke.YKT)
* Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -23,7 +23,7 @@
* OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
* OPL2 ROMs.
*
* version: 1.0.7
* version: 1.0.9
*/
#ifndef YM3438_H
@ -39,9 +39,8 @@ extern "C" {
#define OPN_WRITEBUF_DELAY 15
enum {
ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */
ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */
ym3438_type_ym2612 = 2 /* YM2612 (MD1, MD2 VA2) */
ym3438_mode_ym2612 = 0x01, /* Enables YM2612 emulation (MD1, MD2 VA2) */
ym3438_mode_readmode = 0x02 /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */
};
#include <stdint.h>
@ -80,7 +79,7 @@ typedef struct
Bit8u write_busy_cnt;
Bit8u write_fm_address;
Bit8u write_fm_data;
Bit8u write_fm_mode_a;
Bit16u write_fm_mode_a;
Bit16u address;
Bit8u data;
Bit8u pin_test_in;
@ -205,6 +204,8 @@ typedef struct
Bit8u pan_l[6], pan_r[6];
Bit8u ams[6];
Bit8u pms[6];
Bit8u status;
Bit32u status_time;
/*EXTRA*/
Bit32u mute[7];
@ -240,7 +241,6 @@ void OPN2_Generate(ym3438_t *chip, Bit16s *buf);
void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf);
void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples);
void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples);
void OPN2_SetOptions(Bit8u flags);
void OPN2_SetMute(ym3438_t *chip, Bit32u mute);
#ifdef __cplusplus

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -22,9 +22,10 @@
#include "nuked/ym3438.h"
#include <cstring>
NukedOPN2::NukedOPN2()
NukedOPN2::NukedOPN2(OPNFamily f)
: OPNChipBaseT(f)
{
OPN2_SetChipType(ym3438_type_asic);
OPN2_SetChipType(ym3438_mode_readmode);
chip = new ym3438_t;
setRate(m_rate, m_clock);
}

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,7 +27,7 @@ class NukedOPN2 final : public OPNChipBaseT<NukedOPN2>
{
void *chip;
public:
NukedOPN2();
explicit NukedOPN2(OPNFamily f);
~NukedOPN2() override;
bool canRunAtPcmRate() const override { return false; }

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -21,6 +21,7 @@
#ifndef ONP_CHIP_BASE_H
#define ONP_CHIP_BASE_H
#include "opn_chip_family.h"
#include <stdint.h>
#include <stddef.h>
@ -39,16 +40,19 @@ extern void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate
class OPNChipBase
{
public:
enum { nativeRate = 53267 };
protected:
uint32_t m_id;
uint32_t m_rate;
uint32_t m_clock;
OPNFamily m_family;
public:
OPNChipBase();
explicit OPNChipBase(OPNFamily f);
virtual ~OPNChipBase();
virtual OPNFamily family() const = 0;
uint32_t clockRate() const;
virtual uint32_t nativeClockRate() const = 0;
uint32_t chipId() const { return m_id; }
void setChipId(uint32_t id) { m_id = id; }
@ -61,6 +65,7 @@ public:
virtual void setRate(uint32_t rate, uint32_t clock) = 0;
virtual uint32_t effectiveRate() const = 0;
virtual uint32_t nativeRate() const = 0;
virtual void reset() = 0;
virtual void writeReg(uint32_t port, uint16_t addr, uint8_t data) = 0;
@ -88,9 +93,12 @@ template <class T>
class OPNChipBaseT : public OPNChipBase
{
public:
OPNChipBaseT();
explicit OPNChipBaseT(OPNFamily f);
virtual ~OPNChipBaseT();
OPNFamily family() const override;
uint32_t nativeClockRate() const override;
bool isRunningAtPcmRate() const override;
bool setRunningAtPcmRate(bool r) override;
#if defined(OPNMIDI_AUDIO_TICK_HANDLER)
@ -99,6 +107,7 @@ public:
virtual void setRate(uint32_t rate, uint32_t clock) override;
uint32_t effectiveRate() const override;
uint32_t nativeRate() const override;
virtual void reset() override;
void generate(int16_t *output, size_t frames) override;
void generateAndMix(int16_t *output, size_t frames) override;
@ -134,10 +143,11 @@ template <class T, unsigned Buffer = 256>
class OPNChipBaseBufferedT : public OPNChipBaseT<T>
{
public:
OPNChipBaseBufferedT()
: OPNChipBaseT<T>(), m_bufferIndex(0) {}
explicit OPNChipBaseBufferedT(OPNFamily f)
: OPNChipBaseT<T>(f), m_bufferIndex(0) {}
virtual ~OPNChipBaseBufferedT()
{}
enum { buffer_size = Buffer };
public:
void reset() override;
void nativeGenerate(int16_t *frame) override;

View file

@ -1,5 +1,6 @@
#include "opn_chip_base.h"
#include <cmath>
#include <cstdio>
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
#include <zita-resampler/vresampler.h>
@ -18,11 +19,11 @@
#endif
/* OPNChipBase */
inline OPNChipBase::OPNChipBase() :
inline OPNChipBase::OPNChipBase(OPNFamily f) :
m_id(0),
m_rate(44100),
m_clock(7670454)
m_clock(7670454),
m_family(f)
{
}
@ -30,11 +31,16 @@ inline OPNChipBase::~OPNChipBase()
{
}
inline uint32_t OPNChipBase::clockRate() const
{
return m_clock;
}
/* OPNChipBaseT */
template <class T>
OPNChipBaseT<T>::OPNChipBaseT()
: OPNChipBase(),
OPNChipBaseT<T>::OPNChipBaseT(OPNFamily f)
: OPNChipBase(f),
m_runningAtPcmRate(false)
#if defined(OPNMIDI_AUDIO_TICK_HANDLER)
,
@ -55,6 +61,18 @@ OPNChipBaseT<T>::~OPNChipBaseT()
#endif
}
template <class T>
OPNFamily OPNChipBaseT<T>::family() const
{
return m_family;
}
template <class T>
uint32_t OPNChipBaseT<T>::nativeClockRate() const
{
return opn2_getNativeClockRate(m_family);
}
template <class T>
bool OPNChipBaseT<T>::isRunningAtPcmRate() const
{
@ -86,9 +104,10 @@ template <class T>
void OPNChipBaseT<T>::setRate(uint32_t rate, uint32_t clock)
{
uint32_t oldRate = m_rate;
uint32_t oldClock = m_clock;
m_rate = rate;
m_clock = clock;
if(rate != oldRate)
if(rate != oldRate || clock != oldClock)
setupResampler(rate);
else
resetResampler();
@ -97,7 +116,13 @@ void OPNChipBaseT<T>::setRate(uint32_t rate, uint32_t clock)
template <class T>
uint32_t OPNChipBaseT<T>::effectiveRate() const
{
return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate;
return m_runningAtPcmRate ? m_rate : opn2_getNativeRate(m_family);
}
template <class T>
uint32_t OPNChipBaseT<T>::nativeRate() const
{
return opn2_getNativeRate(m_family);
}
template <class T>
@ -184,7 +209,8 @@ template <class T>
void OPNChipBaseT<T>::setupResampler(uint32_t rate)
{
#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER)
m_resampler->setup(rate * (1.0 / 53267), 2, 48);
double ratio = rate * (1.0 / opn2_getNativeRate(m_family));
m_resampler->setup(ratio, 2, 48);
#else
m_oldsamples[0] = m_oldsamples[1] = 0;
m_samples[0] = m_samples[1] = 0;
@ -238,8 +264,8 @@ void OPNChipBaseT<T>::resampledGenerate(int32_t *output)
rsm->out_count = 1;
rsm->out_data = f_out;
}
output[0] = static_cast<int32_t>(std::lround(f_out[0]));
output[1] = static_cast<int32_t>(std::lround(f_out[1]));
output[0] = static_cast<int32_t>(lround(f_out[0]));
output[1] = static_cast<int32_t>(lround(f_out[1]));
}
#else
template <class T>

View file

@ -0,0 +1,84 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef OPN_CHIP_FAMILY_H
#define OPN_CHIP_FAMILY_H
#include <stdint.h>
#define OPN_FAMILY_EACH(F) \
F(OPN2) F(OPNA)
enum OPNFamily
{
#define Each(x) OPNChip_##x,
OPN_FAMILY_EACH(Each)
#undef Each
OPNChip_Count
};
template <OPNFamily F>
struct OPNFamilyTraits;
template <>
struct OPNFamilyTraits<OPNChip_OPN2>
{
enum
{
nativeRate = 53267,
nativeClockRate = 7670454
};
};
template <>
struct OPNFamilyTraits<OPNChip_OPNA>
{
enum
{
nativeRate = 55466,
nativeClockRate = 7987200
};
};
inline uint32_t opn2_getNativeRate(OPNFamily f)
{
switch(f)
{
default:
#define Each(x) case OPNChip_##x: \
return OPNFamilyTraits<OPNChip_##x>::nativeRate;
OPN_FAMILY_EACH(Each)
#undef Each
}
}
inline uint32_t opn2_getNativeClockRate(OPNFamily f)
{
switch(f)
{
default:
#define Each(x) case OPNChip_##x: \
return OPNFamilyTraits<OPNChip_##x>::nativeClockRate;
OPN_FAMILY_EACH(Each)
#undef Each
}
}
#endif // OPN_CHIP_FAMILY_H

144
thirdparty/opnmidi/chips/pmdwin/op.h vendored Normal file
View file

@ -0,0 +1,144 @@
#ifndef FM_OP_H
#define FM_OP_H
#include <stdint.h>
#define false 0
#define true 1
#define Max(a,b) ((a>b)?a:b)
#define Min(a,b) ((a<b)?a:b)
/* Types ---------------------------------------------------------------- */
typedef int32_t Sample;
typedef int32_t ISample;
/* ---------------------------------------------------------------------------
// Various implementation-specific constants.
// Most are used to either define the bit-width of counters, or the size
// of various constant tables.
*/
#define FM_LFOBITS 8
#define FM_LFOENTS (1 << FM_LFOBITS)
#define FM_OPSINBITS 10
#define FM_OPSINENTS (1 << FM_OPSINBITS)
#define FM_EGBITS 16
#define FM_EGCBITS 18
#define FM_LFOCBITS 14
#define FM_PGBITS 9
#define FM_RATIOBITS 12
typedef enum _EGPhase { next, attack, decay, sustain, release, off } EGPhase;
static inline int Limit(int v, int max, int min)
{
return v > max ? max : (v < min ? min : v);
}
static inline int16_t Limit16(int a)
{
if ((a+0x8000) & ~0xFFFF) return (a>>31) ^ 0x7FFF;
else return a;
}
/* ------------------------------------------------------------------------- */
struct _OPNA;
struct Channel4;
/* Operator ---------------------------------------------------------------- */
typedef struct _FMOperator
{
struct Channel4 *master;
int32_t out, out2;
/* Phase Generator ----------------------------------------------------- */
uint32_t dp; /* Octave (used to define note in conjunction with bn, below). */
uint8_t detune; /* Detune */
uint8_t multiple; /* Multiple */
uint32_t pgcount; /* Phase generator sweep value. Only the top 9 bits are relevant/used. */
uint32_t pgdcount; /* Phase generator increment-per-clock value. Hopefully self-explanatory. */
uint32_t pgdcountl; /* Phase generator detune increment value. Used in the implementation of vibrato. */
/* Equal to pgdcount >> 11. */
/* Envelope Generator -------------------------------------------------- */
uint32_t bn; /* Block/Note */
uint32_t egout;
int eglevel; /* EG ¤Î½ÐÎÏÃÍ */
int eglvnext; /* ¼¡¤Î phase ¤Ë°Ü¤ëÃÍ */
int32_t egstep; /* EG ¤Î¼¡¤ÎÊѰܤޤǤλþ´Ö */
int32_t egstepd; /* egstep ¤Î»þ´Öº¹Ê¬ */
uint8_t egtransa; /* EG ÊѲ½¤Î³ä¹ç (for attack) */
uint8_t egtransd; /* EG ÊѲ½¤Î³ä¹ç (for decay) */
uint32_t ksr; /* key scale rate */
EGPhase phase;
uint8_t ams;
uint8_t ms;
uint8_t keyon; /* current key state */
uint8_t tl; /* Total Level (0-127) */
uint8_t tll; /* Total Level Latch (for CSM mode) */
uint8_t ar; /* Attack Rate (0-63) */
uint8_t dr; /* Decay Rate (0-63) */
uint8_t sr; /* Sustain Rate (0-63) */
uint8_t sl; /* Sustain Level (0-127) */
uint8_t rr; /* Release Rate (0-63) */
uint8_t ks; /* Keyscale (0-3) */
uint8_t ssgtype; /* SSG-Type Envelope Control */
uint8_t amon; /* enable Amplitude Modulation */
uint8_t paramchanged; /* Set whenever f-number or any ADSR constants */
/* are set in OPNASetReg(), as well as upon */
/* chip reset and chip "DAC" samplerate change. */
/* Causes the envelope generator to reset its */
/* internal state, also sets correct increments */
/* for the phase generator. */
uint8_t mute;
} FMOperator;
/* 4-op Channel ------------------------------------------------------------ */
typedef struct Channel4
{
struct _OPNA *master;
uint32_t fb;
int buf[4];
uint8_t idx[6];
int *pms;
uint16_t panl;
uint16_t panr;
FMOperator op[4];
} Channel4;
/* OPNA Rhythm Generator --------------------------------------------------- */
typedef struct Rhythm {
uint8_t pan;
int8_t level;
int8_t volume;
int8_t* sample; /* Rhythm sample data */
uint32_t size; /* Rhythm sample data size */
uint32_t pos; /* Current index into rhytm sample data array */
uint32_t step; /* Amount to increment the above by every time */
/* RhythmMix() gets called. */
uint32_t rate; /* Samplerate of rhythm sample data */
/* (44100Hz in this implementation). */
} Rhythm;
#ifdef __cplusplus
extern "C" {
#endif
/* --------------------------------------------------------------------------
// Miscellaneous and probably irrelevant function prototypes. */
void OperatorInit(Channel4 *ch4, FMOperator *op);
void OperatorReset(FMOperator *op);
void OperatorPrepare(FMOperator *op);
static inline uint32_t IsOn(FMOperator *op) {
return (op->phase - off);
}
#ifdef __cplusplus
}
#endif
#endif

1370
thirdparty/opnmidi/chips/pmdwin/opna.c vendored Normal file

File diff suppressed because it is too large Load diff

91
thirdparty/opnmidi/chips/pmdwin/opna.h vendored Normal file
View file

@ -0,0 +1,91 @@
#ifndef __OPNA_H__
#define __OPNA_H__
#include <stdint.h>
#include "op.h"
#include "psg.h"
/* YM2608 (OPNA) ------------------------------------------------------ */
typedef struct _OPNA
{
int fmvolume;
uint32_t clock;
uint32_t rate;
uint32_t psgrate;
uint32_t status;
Channel4 ch[6];
Channel4* csmch;
int32_t mixdelta;
int mpratio;
uint8_t interpolation;
uint8_t timer_status;
uint8_t regtc;
uint8_t regta[2];
int32_t timera, timera_count;
int32_t timerb, timerb_count;
int32_t timer_step;
uint8_t prescale;
uint8_t devmask;
PSG psg;
Rhythm rhythm[6];
int8_t rhythmtl;
int rhythmtvol;
uint8_t rhythmkey;
int32_t mixl, mixl1;
int32_t mixr, mixr1;
uint8_t fnum2[9];
uint8_t reg22;
uint32_t reg29;
uint32_t statusnext;
uint32_t lfocount;
uint32_t lfodcount;
uint32_t lfotab[8];
uint32_t fnum[6];
uint32_t fnum3[3];
uint8_t aml;
uint32_t currentratio;
float rr;
uint32_t ratetable[64];
} OPNA;
#ifdef __cplusplus
extern "C" {
#endif
/* --------------------------------------------------------------------------- */
uint8_t OPNAInit(OPNA *opna, uint32_t c, uint32_t r, uint8_t ipflag);
void OPNAReset(OPNA *opna);
void OPNASetVolumeRhythm(OPNA *opna, int index, int db);
uint8_t OPNASetRate(OPNA *opna, uint32_t r, uint8_t ipflag);
void OPNASetChannelMask(OPNA *opna, uint32_t mask);
void OPNASetReg(OPNA *opna, uint32_t addr, uint32_t data);
void OPNASetPan(OPNA *opna, uint32_t chan, uint32_t data);
uint8_t OPNATimerCount(OPNA *opna, int32_t us);
void OPNAMix(OPNA *opna, int16_t *buffer, uint32_t nframes);
/* --------------------------------------------------------------------------- */
static inline uint32_t OPNAReadStatus(OPNA *opna) { return opna->status & 0x03; }
static inline int32_t OPNAGetNextEvent(OPNA *opna)
{
uint32_t ta = ((opna->timera_count + 0xffff) >> 16) - 1;
uint32_t tb = ((opna->timerb_count + 0xfff) >> 12) - 1;
return (ta < tb ? ta : tb) + 1;
}
#ifdef __cplusplus
}
#endif
#endif /* FM_OPNA_H */

343
thirdparty/opnmidi/chips/pmdwin/psg.c vendored Normal file
View file

@ -0,0 +1,343 @@
/* FIXME: move ugly-ass legalese somewhere where it won't be seen
// by anyone other than lawyers. (/dev/null would be ideal but sadly
// we live in an imperfect world). */
/* Copyright (c) 2012/2013, Peter Barfuss
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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. */
/* Quick, somewhat hacky PSG implementation. Seems to work fine in most cases.
// Known bugs: volume *may* still be off for a lot of samples. Importantly,
// waveform volume is too quiet but setting it at the correct volume makes
// the noise volume too loud and vice-versa. I *think* what I have currently
// is mostly correct (I'm basing this mostly on how good Strawberry Crisis
// sounds with the given settings), but it's possible that more fine-tuning
// is needed. Apart from that, this is probably the sketchiest part of all
// of my emulator code, but then again there's a bit-exact VHDL core of
// the YM2149F/AY-3-8910, so while I do want to make this as good
// as the code in opna.c, it's the lowest-priority of all of the code here.
// --bofh */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "op.h"
#include "psg.h"
/* ---------------------------------------------------------------------------
// ????
*/
int EmitTable[0x20] = { -1, };
uint32_t enveloptable[16][64] = { { 0, }, };
/* ---------------------------------------------------------------------------
// PSG reset to power-on defaults
*/
void PSGReset(PSG *psg)
{
int i;
for (i=0; i<14; i++)
PSGSetReg(psg, i, 0);
PSGSetReg(psg, 7, 0xff);
PSGSetReg(psg, 14, 0xff);
PSGSetReg(psg, 15, 0xff);
}
/* ---------------------------------------------------------------------------
// This code is strongly inspired by some random PSG emulator code I found,
// and is probably not the optimal way to define periods. It *is* at least
// among the fastest, given that it uses the hilarious hack of using the
// integer overflow on a 32-bit unsigned integer to compute ""moduli"".
*/
void PSGSetClock(PSG *psg, uint32_t clock, uint32_t rate)
{
int tmp;
psg->tperiodbase = (uint32_t)((1 << toneshift ) / 4.0f * clock / rate);
psg->eperiodbase = (uint32_t)((1 << envshift ) / 4.0f * clock / rate);
tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff);
psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff);
psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff);
psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
tmp = psg->reg[6] & 0x1f;
psg->nperiod = tmp;
tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff);
psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2;
}
/* ---------------------------------------------------------------------------
// ????????????
*/
static uint8_t table3[4] = { 0, 1, -1, 0 };
void MakeEnvelopTable(void)
{
/* 0 lo 1 up 2 down 3 hi */
static uint8_t table1[16*2] =
{
2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0,
2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0,
};
int i, j;
if (!enveloptable[0][0]) {
uint32_t *ptr = enveloptable[0];
for (i=0; i<16*2; i++) {
uint8_t v = ((table1[i] & 0x2) ? 31 : 0);
for (j=0; j<32; j++) {
*ptr++ = EmitTable[v];
v += table3[table1[i]];
}
}
}
}
/* ---------------------------------------------------------------------------
// Sets the channel output mask for the PSG device.
// c is a bitvector where the 3 LSBs are set to 0 to disable a given
// PSG channel and 1 to enable it.
// TODO: Possibly allow enabling tone/noise output for each channel independently?
*/
void PSGSetChannelMask(PSG *psg, int c)
{
int i;
psg->mask = c;
for (i=0; i<3; i++)
psg->olevel[i] = psg->mask & (1 << i) ? EmitTable[(psg->reg[8+i] & 15) * 2 + 1] : 0;
}
/* ---------------------------------------------------------------------------
// PSG register set routine. Mostly just what you'd expect from reading the manual.
// Fairly boring code overall. regnum can be 0 - 15, data can be 0x00 - 0xFF.
// (This should not be surprising - the YM2149F *did* use an 8-bit bus, after all).
// Interesting quirk: the task of register 7 (channel enable/disable) is basically
// entirely duplicated by other registers, to the point where you can basically
// just ignore any writes to register 7 entirely. I save it here in case some
// braindead routine wants to read its value and do something based on that
// (Another curiosity: register 7 on the PSG appears to be the only register
// between *both* the OPNA and the PSG which is actually *read from* by
// pmdwin.cpp and not just written to. Amusingly enough, the only reason
// that it is ever read is so that it can then OR something with what it just read
// and then write that back to register 7. Hilarity).
// HACK ALERT: The output levels for channels 0 and 1 are increased by a factor of 4
// to make them match the actual chip in loudness, but without causing the noise channel
// to overtake everything in intensity. This is almost certainly wrong, and moreover
// it assumes that channel 2 will be playing back Speak Board effects which usually means
// drum kit only (for the most part, at least), and not being used as a separate tonal
// channel in its own right. To the best of my knowledge, this does hold for all of ZUN's
// songs, however, once you step outside that set of music, it's trivial to find
// all sorts of counterexamples to that assumption. Therefore, this should be fixed ASAP.
*/
void PSGSetReg(PSG *psg, uint8_t regnum, uint8_t data)
{
if (regnum < 0x10)
{
psg->reg[regnum] = data;
switch (regnum)
{
int tmp;
case 0: /* ChA Fine Tune */
case 1: /* ChA Coarse Tune */
tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff);
psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
break;
case 2: /* ChB Fine Tune */
case 3: /* ChB Coarse Tune */
tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff);
psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
break;
case 4: /* ChC Fine Tune */
case 5: /* ChC Coarse Tune */
tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff);
psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
break;
case 6: /* Noise generator control */
data &= 0x1f;
psg->nperiod = data;
break;
case 8:
psg->olevel[0] = psg->mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 9:
psg->olevel[1] = psg->mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 10:
psg->olevel[2] = psg->mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0;
break;
case 11: /* Envelope period */
case 12:
tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff);
psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2;
break;
case 13: /* Envelope shape */
psg->ecount = 0;
psg->envelop = enveloptable[data & 15];
break;
}
}
}
/* ---------------------------------------------------------------------------
// Init code. Set volume to 0, reset the chip, enable all channels, seed the RNG.
// RNG seed lifted from MAME's YM2149F emulation routine, appears to be correct.
*/
void PSGInit(PSG *psg)
{
int i;
float base = 0x4000 / 3.0f;
for (i=31; i>=2; i--)
{
EmitTable[i] = lrintf(base);
base *= 0.840896415f; /* 1.0f / 1.189207115f */
}
EmitTable[1] = 0;
EmitTable[0] = 0;
MakeEnvelopTable();
PSGSetChannelMask(psg, psg->mask);
psg->rng = 14231;
psg->ncount = 0;
PSGReset(psg);
psg->mask = 0x3f;
}
/* ---------------------------------------------------------------------------
// The main output routine for the PSG emulation.
// dest should be an array of size nsamples, and of type Sample
// (one of int16_t, int32_t or float - any will work here without causing
// clipping/precision problems).
// Everything is implemented using some form of fixed-point arithmetic
// that currently needs no more than 32-bits for its implementation,
// but I'm pretty certain that you can get by with much less than that
// and still have mostly correct-to-fully-correct emulation.
//
// TODO: In the future, test the veracity of the above statement. Moreover,
// if it turns out to be correct, rewrite this routine to not use more than
// the required precision. This is irrelevant for any PC newer than, well,
// a 386DX/68040, but important for efficient hardware implementation.
*/
void PSGMix(PSG *psg, int32_t *dest, uint32_t nsamples)
{
uint8_t chenable[3];
uint8_t r7 = ~psg->reg[7];
unsigned int i, k = 0;
int x, y, z;
if ((r7 & 0x3f) | ((psg->reg[8] | psg->reg[9] | psg->reg[10]) & 0x1f)) {
int noise, sample;
uint32_t env;
uint32_t* p1;
uint32_t* p2;
uint32_t* p3;
chenable[0] = (r7 & 0x01) && (psg->speriod[0] <= (1 << toneshift));
chenable[1] = (r7 & 0x02) && (psg->speriod[1] <= (1 << toneshift));
chenable[2] = (r7 & 0x04) && (psg->speriod[2] <= (1 << toneshift));
p1 = ((psg->mask & 1) && (psg->reg[ 8] & 0x10)) ? &env : &psg->olevel[0];
p2 = ((psg->mask & 2) && (psg->reg[ 9] & 0x10)) ? &env : &psg->olevel[1];
p3 = ((psg->mask & 4) && (psg->reg[10] & 0x10)) ? &env : &psg->olevel[2];
#define SCOUNT(ch) (psg->scount[ch] >> toneshift)
if (p1 != &env && p2 != &env && p3 != &env) {
for (i=0; i<nsamples; i++) {
psg->ncount++;
if(psg->ncount >= psg->nperiod) {
if(psg->rng & 1)
psg->rng ^= 0x24000;
psg->rng >>= 1;
psg->ncount = 0;
}
noise = (psg->rng & 1);
sample = 0;
{
x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; /* 0 or -1 */
sample += (psg->olevel[0] + x) ^ x;
psg->scount[0] += psg->speriod[0];
y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1;
sample += (psg->olevel[1] + y) ^ y;
psg->scount[1] += psg->speriod[1];
/*z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;*/
z = ((r7 >> 5) & noise) - 1;
sample += (psg->olevel[2] + z) ^ z;
psg->scount[2] += psg->speriod[2];
}
sample = Limit16(sample);
dest[k++] += sample;
dest[k++] += sample;
}
psg->ecount = (psg->ecount >> 8) + (psg->eperiod >> 8) * nsamples;
if (psg->ecount >= (1 << (envshift+6-8))) {
if ((psg->reg[0x0d] & 0x0b) != 0x0a)
psg->ecount |= (1 << (envshift+5-8));
psg->ecount &= (1 << (envshift+6-8)) - 1;
}
psg->ecount <<= 8;
} else {
for (i=0; i<nsamples; i++) {
psg->ncount++;
if(psg->ncount >= psg->nperiod) {
if(psg->rng & 1)
psg->rng ^= 0x24000;
psg->rng >>= 1;
psg->ncount = 0;
}
noise = (psg->rng & 1);
sample = 0;
{
env = psg->envelop[psg->ecount >> envshift];
psg->ecount += psg->eperiod;
if (psg->ecount >= (1 << (envshift+6))) {
if ((psg->reg[0x0d] & 0x0b) != 0x0a)
psg->ecount |= (1 << (envshift+5));
psg->ecount &= (1 << (envshift+6)) - 1;
}
x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; /* 0 or -1 */
sample += (*p1 + x) ^ x;
psg->scount[0] += psg->speriod[0];
y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1;
sample += (*p2 + y) ^ y;
psg->scount[1] += psg->speriod[1];
/*z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;*/
z = ((r7 >> 5) & noise) - 1;
sample += (*p3 + z) ^ z;
psg->scount[2] += psg->speriod[2];
}
sample = Limit16(sample);
dest[k++] += sample;
dest[k++] += sample;
}
}
}
}

52
thirdparty/opnmidi/chips/pmdwin/psg.h vendored Normal file
View file

@ -0,0 +1,52 @@
#ifndef __PSG_H__
#define __PSG_H__
#include <stdint.h>
#define PSG_SAMPLETYPE int32_t /* any of int16_t, int32_t or float will work here. */
/* Constants for the shift amounts used in the counters.
*/
enum {
toneshift = 24,
envshift = 22
};
typedef struct _PSG {
uint8_t reg[16];
const uint32_t *envelop;
uint32_t rng;
uint32_t olevel[3];
uint32_t scount[3], speriod[3];
uint32_t ecount, eperiod;
uint32_t ncount, nperiod;
uint32_t tperiodbase;
uint32_t eperiodbase;
int volume;
int mask;
} PSG;
#ifdef __cplusplus
extern "C" {
#endif
/* Mostly self-explanatory.
// Actual descriptions of each function can be found in psg.c
// Also, PSGGetReg() is basically useless.
// (More info on that can *also* be found in psg.c). */
void PSGInit(PSG *psg);
void PSGReset(PSG *psg);
void PSGSetClock(PSG *psg, uint32_t clock, uint32_t rate);
void PSGSetChannelMask(PSG *psg, int c);
void PSGSetReg(PSG *psg, uint8_t regnum, uint8_t data);
void PSGMix(PSG *psg, int32_t *dest, uint32_t nsamples);
static inline uint32_t PSGGetReg(PSG *psg, uint8_t regnum) {
return psg->reg[regnum & 0x0f];
}
#ifdef __cplusplus
}
#endif
#endif /* PSG_H */

View file

@ -0,0 +1,5 @@
#include "rhythmdata.h"
const unsigned char* rhythmdata[6] = {
BD_2608, SD_2608, TOP_2608, HH_2608, TOM_2608, RIM_2608
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,84 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pmdwin_opna.h"
#include "pmdwin/opna.h"
#include <cstring>
#include <cassert>
PMDWinOPNA::PMDWinOPNA(OPNFamily f)
: OPNChipBaseBufferedT(f)
{
OPNA *opn = new OPNA;
chip = reinterpret_cast<ChipType *>(opn);
setRate(m_rate, m_clock);
}
PMDWinOPNA::~PMDWinOPNA()
{
OPNA *opn = reinterpret_cast<OPNA *>(chip);
delete opn;
}
void PMDWinOPNA::setRate(uint32_t rate, uint32_t clock)
{
OPNA *opn = reinterpret_cast<OPNA *>(chip);
OPNChipBaseBufferedT::setRate(rate, clock);
uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate();
std::memset(chip, 0, sizeof(*opn));
OPNAInit(opn, m_clock, chipRate, 0);
OPNASetReg(opn, 0x29, 0x9f);
}
void PMDWinOPNA::reset()
{
OPNChipBaseBufferedT::reset();
OPNA *opn = reinterpret_cast<OPNA *>(chip);
OPNAReset(opn);
OPNASetReg(opn, 0x29, 0x9f);
}
void PMDWinOPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
OPNA *opn = reinterpret_cast<OPNA *>(chip);
OPNASetReg(opn, (port << 8) | addr, data);
}
void PMDWinOPNA::writePan(uint16_t chan, uint8_t data)
{
OPNA *opn = reinterpret_cast<OPNA *>(chip);
OPNASetPan(opn, chan, data);
}
void PMDWinOPNA::nativeGenerateN(int16_t *output, size_t frames)
{
// be cautious to avoid overflowing stack buffer on PMDWin side!
// (on OPNChipBaseBuffered it's fine)
assert(frames < 16384 / 2);
OPNA *opn = reinterpret_cast<OPNA *>(chip);
OPNAMix(opn, output, static_cast<uint32_t>(frames));
}
const char *PMDWinOPNA::emulatorName()
{
return "PMDWin OPNA"; // git 2018-05-11 rev 255ef52
}

45
thirdparty/opnmidi/chips/pmdwin_opna.h vendored Normal file
View file

@ -0,0 +1,45 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2020 Vitaly Novichkov (Wohlstand)
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PMDWIN_OPNA_H
#define PMDWIN_OPNA_H
#include "opn_chip_base.h"
class PMDWinOPNA final : public OPNChipBaseBufferedT<PMDWinOPNA>
{
struct ChipType;
ChipType *chip;
public:
explicit PMDWinOPNA(OPNFamily f);
~PMDWinOPNA() override;
bool canRunAtPcmRate() const override { return true; }
void setRate(uint32_t rate, uint32_t clock) override;
void reset() override;
void writeReg(uint32_t port, uint16_t addr, uint8_t data) override;
void writePan(uint16_t chan, uint8_t data) override;
void nativePreGenerate() override {}
void nativePostGenerate() override {}
void nativeGenerateN(int16_t *output, size_t frames) override;
const char *emulatorName() override;
};
#endif

View file

@ -1,7 +1,7 @@
/*
* FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block
*
* Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
@ -140,6 +140,7 @@ public:
{
switch(rel_to)
{
default:
case SET:
m_mp_tell = static_cast<size_t>(pos);
break;

220
thirdparty/opnmidi/fraction.hpp vendored Normal file
View file

@ -0,0 +1,220 @@
/*
* Fraction number handling.
* Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/)
*
* The license of this file is in Public Domain:
* https://bisqwit.iki.fi/src/index.html
*
* "... and orphan source code files are copyrighted public domain."
*/
#ifndef bqw_fraction_h
#define bqw_fraction_h
#include <cmath>
#include <limits>
template<typename inttype=int>
class fraction
{
inttype num1, num2;
typedef fraction<inttype> self;
void Optim();
#if 1
inline void Debug(char, const self &) { }
#else
inline void Debug(char op, const self &b)
{
cerr << nom() << '/' << denom() << ' ' << op
<< ' ' << b.nom() << '/' << denom()
<< ":\n";
}
#endif
public:
void set(inttype n, inttype d) { num1=n; num2=d; Optim(); }
fraction() : num1(0), num2(1) { }
fraction(inttype value) : num1(value), num2(1) { }
fraction(inttype n, inttype d) : num1(n), num2(d) { }
fraction(int value) : num1(value), num2(1) { }
template<typename floattype>
explicit fraction(const floattype value) { operator= (value); }
inline double value() const {return nom() / (double)denom(); }
inline long double valuel() const {return nom() / (long double)denom(); }
self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; }
self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; }
self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; }
self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; }
self &operator+= (const self &b);
self &operator-= (const self &b);
self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; }
self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; }
self operator- () const { return self(-num1, num2); }
#define fraction_blah_func(op1, op2) \
self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; }
fraction_blah_func( +, += )
fraction_blah_func( -, -= )
fraction_blah_func( /, /= )
fraction_blah_func( *, *= )
#undef fraction_blah_func
#define fraction_blah_func(op) \
bool operator op(const self &b) const { return value() op b.value(); } \
bool operator op(inttype b) const { return value() op b; }
fraction_blah_func( < )
fraction_blah_func( > )
fraction_blah_func( <= )
fraction_blah_func( >= )
#undef fraction_blah_func
const inttype &nom() const { return num1; }
const inttype &denom() const { return num2; }
inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; }
inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; }
inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); }
inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); }
//operator bool () const { return nom() != 0; }
inline bool negative() const { return (nom() < 0) ^ (denom() < 0); }
self &operator= (const inttype value) { num2=1; num1=value; return *this; }
//self &operator= (int value) { num2=1; num1=value; return *this; }
self &operator= (double orig) { return *this = (long double)orig; }
self &operator= (long double orig);
};
#ifdef _MSC_VER
#pragma warning(disable:4146)
#endif
template<typename inttype>
void fraction<inttype>::Optim()
{
/* Euclidean algorithm */
inttype n1, n2, nn1, nn2;
nn1 = std::numeric_limits<inttype>::is_signed ? (num1 >= 0 ? num1 : -num1) : num1;
nn2 = std::numeric_limits<inttype>::is_signed ? (num2 >= 0 ? num2 : -num2) : num2;
if(nn1 < nn2)
n1 = num1, n2 = num2;
else
n1 = num2, n2 = num1;
if(!num1) { num2 = 1; return; }
for(;;)
{
//fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2);
inttype tmp = n2 % n1;
if(!tmp)break;
n2 = n1;
n1 = tmp;
}
num1 /= n1;
num2 /= n1;
//fprintf(stderr, "result: %d/%d\n\n", nom(), denom());
}
#ifdef _MSC_VER
#pragma warning(default:4146)
#endif
template<typename inttype>
inline const fraction<inttype> abs(const fraction<inttype> &f)
{
return fraction<inttype>(abs(f.nom()), abs(f.denom()));
}
#define fraction_blah_func(op) \
template<typename inttype> \
fraction<inttype> operator op \
(const inttype bla, const fraction<inttype> &b) \
{ return fraction<inttype> (bla) op b; }
fraction_blah_func( + )
fraction_blah_func( - )
fraction_blah_func( * )
fraction_blah_func( / )
#undef fraction_blah_func
#define fraction_blah_func(op1, op2) \
template<typename inttype> \
fraction<inttype> &fraction<inttype>::operator op2 (const fraction<inttype> &b) \
{ \
inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \
num2 *= b.denom(); \
num1 = newnom; \
Optim(); \
return *this; \
}
fraction_blah_func( +, += )
fraction_blah_func( -, -= )
#undef fraction_blah_func
template<typename inttype>
fraction<inttype> &fraction<inttype>::operator= (long double orig)
{
if(orig == 0.0)
{
set(0, 0);
return *this;
}
inttype cf[25];
for(int maxdepth=1; maxdepth<25; ++maxdepth)
{
inttype u,v;
long double virhe, a=orig;
int i, viim;
for(i = 0; i < maxdepth; ++i)
{
cf[i] = (inttype)a;
if(cf[i]-1 > cf[i])break;
a = 1.0 / (a - cf[i]);
}
for(viim=i-1; i < maxdepth; ++i)
cf[i] = 0;
u = cf[viim];
v = 1;
for(i = viim-1; i >= 0; --i)
{
inttype w = cf[i] * u + v;
v = u;
u = w;
}
virhe = (orig - (u / (long double)v)) / orig;
set(u, v);
//if(verbose > 4)
// cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n";
if(virhe < 1e-8 && virhe > -1e-8)break;
}
//if(verbose > 4)
//{
// cerr << "Fraction=" << orig << ": " << *this << endl;
//}
return *this;
}
/*
template<typename inttype>
ostream &operator << (ostream &dest, const fraction<inttype> &m)
{
if(m.denom() == (inttype)1) return dest << m.nom();
return dest << m.nom() << '/' << m.denom();
}
*/
#endif

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2016 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2016-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -48,7 +48,7 @@ enum
OPERATOR1 = 0,
OPERATOR2 = 1,
OPERATOR3 = 2,
OPERATOR4 = 3,
OPERATOR4 = 3
};
/* *********** FM Operator indexes *end******* */
@ -76,47 +76,46 @@ struct OPN_Operator
};
OPNDATA_BYTE_COMPARABLE(struct OPN_Operator)
struct opnInstData
struct OpnTimbre
{
//! Operators prepared for sending to OPL chip emulator
OPN_Operator OPS[4];
//! Feedback / algorithm
uint8_t fbalg;
//! LFO sensitivity
uint8_t lfosens;
//! Note offset
int16_t finetune;
//! Semi-tone offset
int16_t noteOffset;
};
OPNDATA_BYTE_COMPARABLE(struct opnInstData)
struct opnInstMeta
{
enum { Flag_Pseudo8op = 0x01, Flag_NoSound = 0x02 };
uint16_t opnno1, opnno2;
uint8_t tone;
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double fine_tune;
};
OPNDATA_BYTE_COMPARABLE(struct opnInstMeta)
OPNDATA_BYTE_COMPARABLE(struct OpnTimbre)
/**
* @brief Instrument data with operators included
*/
struct opnInstMeta2
struct OpnInstMeta
{
opnInstData opn[2];
uint8_t tone;
enum
{
Flag_Pseudo8op = 0x01,
Flag_NoSound = 0x02
};
//! Operator data
OpnTimbre op[2];
//! Fixed note for drum instruments
uint8_t drumTone;
//! Instrument flags
uint8_t flags;
uint16_t ms_sound_kon; // Number of milliseconds it produces sound;
uint16_t ms_sound_koff;
double fine_tune;
int8_t midi_velocity_offset;
#if 0
opnInstMeta2() {}
explicit opnInstMeta2(const opnInstMeta &d);
#endif
//! Number of milliseconds it produces sound while key on
uint16_t soundKeyOnMs;
//! Number of milliseconds it produces sound while releasing after key off
uint16_t soundKeyOffMs;
//! MIDI velocity offset
int8_t midiVelocityOffset;
//! Second voice detune
double voice2_fine_tune;
};
OPNDATA_BYTE_COMPARABLE(struct opnInstMeta2)
OPNDATA_BYTE_COMPARABLE(struct OpnInstMeta)
#undef OPNDATA_BYTE_COMPARABLE
#pragma pack(pop)
@ -129,30 +128,17 @@ struct OpnBankSetup
int volumeModel;
int lfoEnable;
int lfoFrequency;
int chipType;
};
#if 0
/**
* @brief Conversion of storage formats
*/
inline opnInstMeta2::opnInstMeta2(const opnInstMeta &d)
: tone(d.tone), flags(d.flags),
ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff),
fine_tune(d.fine_tune), midi_velocity_offset(d.midi_velocity_offset)
{
opn[0] = ::opn[d.opnno1];
opn[1] = ::opn[d.opnno2];
}
#endif
/**
* @brief Convert external instrument to internal instrument
*/
void cvt_OPNI_to_FMIns(opnInstMeta2 &dst, const struct OPN2_Instrument &src);
void cvt_OPNI_to_FMIns(OpnInstMeta &dst, const struct OPN2_Instrument &src);
/**
* @brief Convert internal instrument to external instrument
*/
void cvt_FMIns_to_OPNI(struct OPN2_Instrument &dst, const opnInstMeta2 &src);
void cvt_FMIns_to_OPNI(struct OPN2_Instrument &dst, const OpnInstMeta &src);
#endif // OPNMIDI_OPNBANK_H

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2018 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -21,7 +21,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opnmidi_midiplay.hpp"
#include "opnmidi_opn2.hpp"
#include "opnmidi_private.hpp"
#include "chips/opn_chip_base.h"
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
#include "midi_sequencer.hpp"
#endif
/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */
#define GET_MIDI_PLAYER(device) reinterpret_cast<OPNMIDIplay *>((device)->opn2_midiPlayer)
@ -87,9 +93,10 @@ OPNMIDI_EXPORT int opn2_setNumChips(OPN2_MIDIPlayer *device, int numCards)
return -1;
}
if(!play->m_synth.setupLocked())
Synth &synth = *play->m_synth;
if(!synth.setupLocked())
{
play->m_synth.m_numChips = play->m_setup.numChips;
synth.m_numChips = play->m_setup.numChips;
play->partialReset();
}
@ -111,7 +118,7 @@ OPNMIDI_EXPORT int opn2_getNumChipsObtained(struct OPN2_MIDIPlayer *device)
return -2;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return (int)play->m_synth.m_numChips;
return (int)play->m_synth->m_numChips;
}
@ -121,7 +128,7 @@ OPNMIDI_EXPORT int opn2_reserveBanks(OPN2_MIDIPlayer *device, unsigned banks)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPN2::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
map.reserve(banks);
return (int)map.capacity();
}
@ -134,13 +141,13 @@ OPNMIDI_EXPORT int opn2_getBank(OPN2_MIDIPlayer *device, const OPN2_BankId *idp,
OPN2_BankId id = *idp;
if(id.lsb > 127 || id.msb > 127 || id.percussive > 1)
return -1;
size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPN2::PercussionTag) : 0));
size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(Synth::PercussionTag) : 0));
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPN2::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPN2::BankMap::iterator it;
Synth::BankMap::iterator it;
if(!(flags & OPNMIDI_Bank_Create))
{
it = map.find(idnumber);
@ -149,16 +156,16 @@ OPNMIDI_EXPORT int opn2_getBank(OPN2_MIDIPlayer *device, const OPN2_BankId *idp,
}
else
{
std::pair<size_t, OPN2::Bank> value;
std::pair<size_t, Synth::Bank> value;
value.first = idnumber;
memset(&value.second, 0, sizeof(value.second));
for (unsigned i = 0; i < 128; ++i)
value.second.ins[i].flags = opnInstMeta::Flag_NoSound;
value.second.ins[i].flags = OpnInstMeta::Flag_NoSound;
std::pair<OPN2::BankMap::iterator, bool> ir;
if(flags & OPNMIDI_Bank_CreateRt)
std::pair<Synth::BankMap::iterator, bool> ir;
if((flags & OPNMIDI_Bank_CreateRt) == OPNMIDI_Bank_CreateRt)
{
ir = map.insert(value, OPN2::BankMap::do_not_expand_t());
ir = map.insert(value, Synth::BankMap::do_not_expand_t());
if(ir.first == map.end())
return -1;
}
@ -176,11 +183,11 @@ OPNMIDI_EXPORT int opn2_getBankId(OPN2_MIDIPlayer *device, const OPN2_Bank *bank
if(!device || !bank)
return -1;
OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer);
OPN2::BankMap::key_type idnumber = it->first;
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::key_type idnumber = it->first;
id->msb = (idnumber >> 8) & 127;
id->lsb = idnumber & 127;
id->percussive = (idnumber & OPN2::PercussionTag) ? 1 : 0;
id->percussive = (idnumber & Synth::PercussionTag) ? 1 : 0;
return 0;
}
@ -191,8 +198,8 @@ OPNMIDI_EXPORT int opn2_removeBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPN2::BankMap &map = play->m_synth.m_insBanks;
OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap &map = play->m_synth->m_insBanks;
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
size_t size = map.size();
map.erase(it);
return (map.size() != size) ? 0 : -1;
@ -205,9 +212,9 @@ OPNMIDI_EXPORT int opn2_getFirstBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPN2::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPN2::BankMap::iterator it = map.begin();
Synth::BankMap::iterator it = map.begin();
if(it == map.end())
return -1;
@ -222,9 +229,9 @@ OPNMIDI_EXPORT int opn2_getNextBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
OPN2::BankMap &map = play->m_synth.m_insBanks;
Synth::BankMap &map = play->m_synth->m_insBanks;
OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
if(++it == map.end())
return -1;
@ -237,7 +244,7 @@ OPNMIDI_EXPORT int opn2_getInstrument(OPN2_MIDIPlayer *device, const OPN2_Bank *
if(!device || !bank || index > 127 || !ins)
return -1;
OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_FMIns_to_OPNI(*ins, it->second.ins[index]);
ins->version = 0;
return 0;
@ -251,7 +258,7 @@ OPNMIDI_EXPORT int opn2_setInstrument(OPN2_MIDIPlayer *device, OPN2_Bank *bank,
if(ins->version != 0)
return -1;
OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer);
Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer);
cvt_OPNI_to_FMIns(it->second.ins[index], *ins);
return 0;
}
@ -303,11 +310,12 @@ OPNMIDI_EXPORT void opn2_setLfoEnabled(struct OPN2_MIDIPlayer *device, int lfoEn
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.lfoEnable = lfoEnable;
play->m_synth.m_lfoEnable = (lfoEnable < 0 ?
play->m_synth.m_insBankSetup.lfoEnable :
play->m_setup.lfoEnable) != 0;
play->m_synth.commitLFOSetup();
synth.m_lfoEnable = (lfoEnable < 0 ?
synth.m_insBankSetup.lfoEnable :
play->m_setup.lfoEnable) != 0;
synth.commitLFOSetup();
}
OPNMIDI_EXPORT int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device)
@ -315,7 +323,7 @@ OPNMIDI_EXPORT int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device)
if(!device) return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.m_lfoEnable;
return play->m_synth->m_lfoEnable;
}
OPNMIDI_EXPORT void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfoFrequency)
@ -323,11 +331,12 @@ OPNMIDI_EXPORT void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfo
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.lfoFrequency = lfoFrequency;
play->m_synth.m_lfoFrequency = lfoFrequency < 0 ?
play->m_synth.m_insBankSetup.lfoFrequency :
synth.m_lfoFrequency = lfoFrequency < 0 ?
synth.m_insBankSetup.lfoFrequency :
(uint8_t)play->m_setup.lfoFrequency;
play->m_synth.commitLFOSetup();
synth.commitLFOSetup();
}
OPNMIDI_EXPORT int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device)
@ -335,7 +344,26 @@ OPNMIDI_EXPORT int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device)
if(!device) return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.m_lfoFrequency;
return play->m_synth->m_lfoFrequency;
}
/*Override chip type. -1 - use bank default state*/
OPNMIDI_EXPORT void opn2_setChipType(struct OPN2_MIDIPlayer *device, int chipType)
{
if(!device) return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_setup.chipType = chipType;
play->applySetup();
}
/*Get the chip type*/
OPNMIDI_EXPORT int opn2_getChipType(struct OPN2_MIDIPlayer *device)
{
if(!device) return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth->chipFamily();
}
OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod)
@ -345,7 +373,7 @@ OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_setup.ScaleModulators = smod;
play->m_synth.m_scaleModulators = (play->m_setup.ScaleModulators != 0);
play->m_synth->m_scaleModulators = (play->m_setup.ScaleModulators != 0);
}
OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness)
@ -359,13 +387,14 @@ OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device,
OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return;
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer.setLoopEnabled(loopEn != 0);
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer->setLoopEnabled(loopEn != 0);
#else
ADL_UNUSED(device);
ADL_UNUSED(loopEn);
#endif
}
@ -376,7 +405,7 @@ OPNMIDI_EXPORT void opn2_setSoftPanEnabled(OPN2_MIDIPlayer *device, int softPanE
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_synth.m_softPanning = (softPanEn != 0);
play->m_synth->m_softPanning = (softPanEn != 0);
}
/* !!!DEPRECATED!!! */
@ -386,13 +415,14 @@ OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, i
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.LogarithmicVolumes = static_cast<unsigned int>(logvol);
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
if(play->m_setup.LogarithmicVolumes != 0)
play->m_synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2);
synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2);
else
play->m_synth.setVolumeScaleModel(static_cast<OPNMIDI_VolumeModels>(play->m_setup.VolumeModel));
synth.setVolumeScaleModel(static_cast<OPNMIDI_VolumeModels>(play->m_setup.VolumeModel));
}
}
@ -402,13 +432,14 @@ OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volume
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.VolumeModel = volumeModel;
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
{
if(play->m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO)//Use bank default volume model
play->m_synth.m_volumeScale = (OPN2::VolumesScale)play->m_synth.m_insBankSetup.volumeModel;
synth.m_volumeScale = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel;
else
play->m_synth.setVolumeScaleModel(static_cast<OPNMIDI_VolumeModels>(volumeModel));
synth.setVolumeScaleModel(static_cast<OPNMIDI_VolumeModels>(volumeModel));
}
}
@ -418,7 +449,7 @@ OPNMIDI_EXPORT int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_synth.getVolumeScaleModel();
return play->m_synth->getVolumeScaleModel();
}
OPNMIDI_EXPORT int opn2_openFile(OPN2_MIDIPlayer *device, const char *filePath)
@ -487,8 +518,9 @@ OPNMIDI_EXPORT const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device)
{
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
if(!play->m_synth.m_chips.empty())
return play->m_synth.m_chips[0]->emulatorName();
Synth &synth = *play->m_synth;
if(!synth.m_chips.empty())
return synth.m_chips[0]->emulatorName();
}
return "Unknown";
}
@ -517,8 +549,9 @@ OPNMIDI_EXPORT int opn2_setRunAtPcmRate(OPN2_MIDIPlayer *device, int enabled)
{
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
play->m_setup.runAtPcmRate = (enabled != 0);
if(!play->m_synth.setupLocked())
if(!synth.setupLocked())
play->partialReset();
return 0;
}
@ -584,7 +617,7 @@ OPNMIDI_EXPORT double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.timeLength();
return play->m_sequencer->timeLength();
#else
ADL_UNUSED(device);
return -1.0;
@ -598,7 +631,7 @@ OPNMIDI_EXPORT double opn2_loopStartTime(struct OPN2_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getLoopStart();
return play->m_sequencer->getLoopStart();
#else
ADL_UNUSED(device);
return -1.0;
@ -612,7 +645,7 @@ OPNMIDI_EXPORT double opn2_loopEndTime(struct OPN2_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getLoopEnd();
return play->m_sequencer->getLoopEnd();
#else
ADL_UNUSED(device);
return -1.0;
@ -626,7 +659,7 @@ OPNMIDI_EXPORT double opn2_positionTell(struct OPN2_MIDIPlayer *device)
return -1.0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.tell();
return play->m_sequencer->tell();
#else
ADL_UNUSED(device);
return -1.0;
@ -643,7 +676,7 @@ OPNMIDI_EXPORT void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double sec
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->realTime_panic();
play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay);
play->m_setup.delay = play->m_sequencer->seek(seconds, play->m_setup.mindelay);
play->m_setup.carry = 0.0;
#else
ADL_UNUSED(device);
@ -659,7 +692,7 @@ OPNMIDI_EXPORT void opn2_positionRewind(struct OPN2_MIDIPlayer *device)
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->realTime_panic();
play->m_sequencer.rewind();
play->m_sequencer->rewind();
#else
ADL_UNUSED(device);
#endif
@ -672,7 +705,7 @@ OPNMIDI_EXPORT void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer.setTempo(tempo);
play->m_sequencer->setTempo(tempo);
#else
ADL_UNUSED(device);
ADL_UNUSED(tempo);
@ -698,7 +731,7 @@ OPNMIDI_EXPORT const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device)
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMusicTitle().c_str();
return play->m_sequencer->getMusicTitle().c_str();
#else
ADL_UNUSED(device);
return "";
@ -713,7 +746,7 @@ OPNMIDI_EXPORT const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *devic
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMusicCopyright().c_str();
return play->m_sequencer->getMusicCopyright().c_str();
#else
ADL_UNUSED(device);
return 0;
@ -727,7 +760,7 @@ OPNMIDI_EXPORT size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getTrackTitles().size();
return play->m_sequencer->getTrackTitles().size();
#else
ADL_UNUSED(device);
return 0;
@ -741,7 +774,7 @@ OPNMIDI_EXPORT const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, s
return "";
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
const std::vector<std::string> &titles = play->m_sequencer.getTrackTitles();
const std::vector<std::string> &titles = play->m_sequencer->getTrackTitles();
if(index >= titles.size())
return "INVALID";
return titles[index].c_str();
@ -760,7 +793,7 @@ OPNMIDI_EXPORT size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getMarkers().size();
return play->m_sequencer->getMarkers().size();
#else
ADL_UNUSED(device);
return 0;
@ -783,7 +816,7 @@ OPNMIDI_EXPORT Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device,
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer.getMarkers();
const std::vector<MidiSequencer::MIDI_MarkerEntry> &markers = play->m_sequencer->getMarkers();
if(index >= markers.size())
{
marker.label = "INVALID";
@ -812,8 +845,8 @@ OPNMIDI_EXPORT void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_Ra
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencerInterface.onEvent = rawEventHook;
play->m_sequencerInterface.onEvent_userData = userData;
play->m_sequencerInterface->onEvent = rawEventHook;
play->m_sequencerInterface->onEvent_userData = userData;
#else
ADL_UNUSED(device);
ADL_UNUSED(rawEventHook);
@ -842,8 +875,8 @@ OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN
play->hooks.onDebugMessage = debugMessageHook;
play->hooks.onDebugMessage_userData = userData;
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
play->m_sequencerInterface.onDebugMessage = debugMessageHook;
play->m_sequencerInterface.onDebugMessage_userData = userData;
play->m_sequencerInterface->onDebugMessage = debugMessageHook;
play->m_sequencerInterface->onDebugMessage_userData = userData;
#endif
}
@ -1029,7 +1062,7 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount,
// setup.SkipForward -= 1;
//else
{
if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0))
if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0))
break;//Stop to fetch samples at reaching the song end with disabled loop
ssize_t leftSamples = left / 2;
@ -1046,14 +1079,15 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount,
//fill buffer with zeros
int32_t *out_buf = player->m_outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->m_synth.m_numChips;
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
else/* if(n_periodCountStereo > 0)*/
{
/* Generate data from every chip and mix result */
for(size_t card = 0; card < chips; ++card)
player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
@ -1123,14 +1157,15 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl
//fill buffer with zeros
int32_t *out_buf = player->m_outBuf;
std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0]));
unsigned int chips = player->m_synth.m_numChips;
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
else/* if(n_periodCountStereo > 0)*/
{
/* Generate data from every chip and mix result */
for(size_t card = 0; card < chips; ++card)
player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo);
}
/* Process it */
if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1)
@ -1170,7 +1205,7 @@ OPNMIDI_EXPORT int opn2_atEnd(struct OPN2_MIDIPlayer *device)
return 1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return (int)play->m_sequencer.positionAtEnd();
return (int)play->m_sequencer->positionAtEnd();
#else
ADL_UNUSED(device);
return 1;
@ -1184,7 +1219,7 @@ OPNMIDI_EXPORT size_t opn2_trackCount(struct OPN2_MIDIPlayer *device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer.getTrackCount();
return play->m_sequencer->getTrackCount();
#else
ADL_UNUSED(device);
return 0;
@ -1198,7 +1233,7 @@ OPNMIDI_EXPORT int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t t
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
MidiSequencer &seq = play->m_sequencer;
MidiSequencer &seq = *play->m_sequencer;
unsigned enableFlag = trackOptions & 3;
trackOptions &= ~3u;

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2018 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -29,7 +29,7 @@ extern "C" {
#endif
#define OPNMIDI_VERSION_MAJOR 1
#define OPNMIDI_VERSION_MINOR 4
#define OPNMIDI_VERSION_MINOR 5
#define OPNMIDI_VERSION_PATCHLEVEL 0
#define OPNMIDI_TOSTR_I(s) #s
@ -39,6 +39,9 @@ extern "C" {
OPNMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \
OPNMIDI_TOSTR(OPNMIDI_VERSION_PATCHLEVEL)
#define OPN_OPN2_SAMPLE_RATE 53267
#define OPN_OPNA_SAMPLE_RATE 55466
#include <stddef.h>
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
@ -93,6 +96,17 @@ typedef short OPN2_SInt16;
# define OPNMIDI_DECLSPEC
#endif
/**
* @brief Chip types
*/
enum OPNMIDI_ChipType
{
/*! The Yamaha OPN2, alias YM2612 YM3438 */
OPNMIDI_ChipType_OPN2 = 0,
/*! The Yamaha OPNA, alias YM2608 */
OPNMIDI_ChipType_OPNA
};
/**
* @brief Volume scaling models
*/
@ -138,7 +152,7 @@ enum OPNMIDI_SampleType
/*! unsigned PCM 32-bit */
OPNMIDI_SampleType_U32,
/*! Count of available sample format types */
OPNMIDI_SampleType_Count,
OPNMIDI_SampleType_Count
};
/**
@ -220,7 +234,74 @@ enum OPN2_BankAccessFlags
OPNMIDI_Bank_CreateRt = 1|2
};
typedef struct OPN2_Instrument OPN2_Instrument;
/* ======== Instrument structures ======== */
/**
* @brief Version of the instrument data format
*/
enum
{
OPNMIDI_InstrumentVersion = 0
};
/**
* @brief Instrument flags
*/
typedef enum OPN2_InstrumentFlags
{
OPNMIDI_Ins_Pseudo8op = 0x01, /*Reserved for future use, not implemented yet*/
OPNMIDI_Ins_IsBlank = 0x02
} OPN2_InstrumentFlags;
/**
* @brief Operator structure, part of Instrument structure
*/
typedef struct OPN2_Operator
{
/* Detune and frequency multiplication register data */
OPN2_UInt8 dtfm_30;
/* Total level register data */
OPN2_UInt8 level_40;
/* Rate scale and attack register data */
OPN2_UInt8 rsatk_50;
/* Amplitude modulation enable and Decay-1 register data */
OPN2_UInt8 amdecay1_60;
/* Decay-2 register data */
OPN2_UInt8 decay2_70;
/* Sustain and Release register data */
OPN2_UInt8 susrel_80;
/* SSG-EG register data */
OPN2_UInt8 ssgeg_90;
} OPN2_Operator;
/**
* @brief Instrument structure
*/
typedef struct OPN2_Instrument
{
/*! Version of the instrument object */
int version;
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
OPN2_SInt16 note_offset;
/* Reserved */
OPN2_SInt8 midi_velocity_offset;
/* Percussion MIDI base tone number at which this drum will be played */
OPN2_UInt8 percussion_key_number;
/* Instrument flags */
OPN2_UInt8 inst_flags;
/* Feedback and Algorithm register data */
OPN2_UInt8 fbalg;
/* LFO Sensitivity register data */
OPN2_UInt8 lfosens;
/* Operators register data */
OPN2_Operator operators[4];
/* Millisecond delay of sounding while key is on */
OPN2_UInt16 delay_on_ms;
/* Millisecond delay of sounding after key off */
OPN2_UInt16 delay_off_ms;
} OPN2_Instrument;
@ -311,6 +392,12 @@ extern OPNMIDI_DECLSPEC void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device
/*Get the LFO frequency*/
extern OPNMIDI_DECLSPEC int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device);
/*Override chip type. -1 - use bank default state*/
extern OPNMIDI_DECLSPEC void opn2_setChipType(struct OPN2_MIDIPlayer *device, int chipType);
/*Get the chip type*/
extern OPNMIDI_DECLSPEC int opn2_getChipType(struct OPN2_MIDIPlayer *device);
/**
* @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes
* @param device Instance of the library
@ -419,6 +506,14 @@ enum Opn2_Emulator
OPNMIDI_EMU_GENS,
/*! Genesis Plus GX (a fork of Mame YM2612) */
OPNMIDI_EMU_GX,
/*! Neko Project II OPNA */
OPNMIDI_EMU_NP2,
/*! Mame YM2608 */
OPNMIDI_EMU_MAME_2608,
/*! PMDWin OPNA */
OPNMIDI_EMU_PMDWIN,
/*! VGM file dumper (required for MIDI2VGM) */
OPNMIDI_VGM_DUMPER,
/*! Count instrument on the level */
OPNMIDI_EMU_end
};
@ -502,6 +597,8 @@ extern OPNMIDI_DECLSPEC const char *opn2_errorInfo(struct OPN2_MIDIPlayer *devic
* Tip 1: You can initialize multiple instances and run them in parallel
* Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes
* Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again.
* Top 4: To generate output in OPN2 or OPNA chip native sample rate, please initialize it with sample rate
* value as `OPN_OPN2_SAMPLE_RATE` or `OPN_OPNA_SAMPLE_RATE` in dependence on the chip
*
* @param sample_rate Output sample rate
* @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info.
@ -798,7 +895,7 @@ enum OPNMIDI_TrackOptions
/*! Disabled track */
OPNMIDI_TrackOption_Off = 2,
/*! Solo track */
OPNMIDI_TrackOption_Solo = 3,
OPNMIDI_TrackOption_Solo = 3
};
/**
@ -1002,76 +1099,6 @@ extern OPNMIDI_DECLSPEC void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *de
*/
extern OPNMIDI_DECLSPEC int opn2_describeChannels(struct OPN2_MIDIPlayer *device, char *text, char *attr, size_t size);
/* ======== Instrument structures ======== */
/**
* @brief Version of the instrument data format
*/
enum
{
OPNMIDI_InstrumentVersion = 0
};
/**
* @brief Instrument flags
*/
typedef enum OPN2_InstrumentFlags
{
OPNMIDI_Ins_Pseudo8op = 0x01, /*Reserved for future use, not implemented yet*/
OPNMIDI_Ins_IsBlank = 0x02
} OPN2_InstrumentFlags;
/**
* @brief Operator structure, part of Instrument structure
*/
typedef struct OPN2_Operator
{
/* Detune and frequency multiplication register data */
OPN2_UInt8 dtfm_30;
/* Total level register data */
OPN2_UInt8 level_40;
/* Rate scale and attack register data */
OPN2_UInt8 rsatk_50;
/* Amplitude modulation enable and Decay-1 register data */
OPN2_UInt8 amdecay1_60;
/* Decay-2 register data */
OPN2_UInt8 decay2_70;
/* Sustain and Release register data */
OPN2_UInt8 susrel_80;
/* SSG-EG register data */
OPN2_UInt8 ssgeg_90;
} OPN2_Operator;
/**
* @brief Instrument structure
*/
typedef struct OPN2_Instrument
{
/*! Version of the instrument object */
int version;
/* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */
OPN2_SInt16 note_offset;
/* Reserved */
OPN2_SInt8 midi_velocity_offset;
/* Percussion MIDI base tone number at which this drum will be played */
OPN2_UInt8 percussion_key_number;
/* Instrument flags */
OPN2_UInt8 inst_flags;
/* Feedback and Algorithm register data */
OPN2_UInt8 fbalg;
/* LFO Sensitivity register data */
OPN2_UInt8 lfosens;
/* Operators register data */
OPN2_Operator operators[4];
/* Millisecond delay of sounding while key is on */
OPN2_UInt16 delay_on_ms;
/* Millisecond delay of sounding after key off */
OPN2_UInt16 delay_off_ms;
} OPN2_Instrument;
#ifdef __cplusplus
}
#endif

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -74,7 +74,7 @@ private:
enum
{
hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */
hash_buckets = 1 << hash_bits,
hash_buckets = 1 << hash_bits
};
public:

View file

@ -2,7 +2,7 @@
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -24,59 +24,59 @@
#include "opnbank.h"
template <class WOPNI>
static void cvt_generic_to_FMIns(opnInstMeta2 &ins, const WOPNI &in)
static void cvt_generic_to_FMIns(OpnInstMeta &ins, const WOPNI &in)
{
ins.tone = in.percussion_key_number;
ins.drumTone = in.percussion_key_number;
ins.flags = in.inst_flags;
/* Junk, delete later */
ins.fine_tune = 0.0;
ins.voice2_fine_tune = 0.0;
/* Junk, delete later */
ins.opn[0].fbalg = in.fbalg;
ins.opn[0].lfosens = in.lfosens;
ins.opn[0].finetune = in.note_offset;
ins.midi_velocity_offset = in.midi_velocity_offset;
ins.op[0].fbalg = in.fbalg;
ins.op[0].lfosens = in.lfosens;
ins.op[0].noteOffset = in.note_offset;
ins.midiVelocityOffset = in.midi_velocity_offset;
for(size_t op = 0; op < 4; op++)
{
ins.opn[0].OPS[op].data[0] = in.operators[op].dtfm_30;
ins.opn[0].OPS[op].data[1] = in.operators[op].level_40;
ins.opn[0].OPS[op].data[2] = in.operators[op].rsatk_50;
ins.opn[0].OPS[op].data[3] = in.operators[op].amdecay1_60;
ins.opn[0].OPS[op].data[4] = in.operators[op].decay2_70;
ins.opn[0].OPS[op].data[5] = in.operators[op].susrel_80;
ins.opn[0].OPS[op].data[6] = in.operators[op].ssgeg_90;
ins.op[0].OPS[op].data[0] = in.operators[op].dtfm_30;
ins.op[0].OPS[op].data[1] = in.operators[op].level_40;
ins.op[0].OPS[op].data[2] = in.operators[op].rsatk_50;
ins.op[0].OPS[op].data[3] = in.operators[op].amdecay1_60;
ins.op[0].OPS[op].data[4] = in.operators[op].decay2_70;
ins.op[0].OPS[op].data[5] = in.operators[op].susrel_80;
ins.op[0].OPS[op].data[6] = in.operators[op].ssgeg_90;
}
ins.opn[1] = ins.opn[0];
ins.op[1] = ins.op[0];
ins.ms_sound_kon = in.delay_on_ms;
ins.ms_sound_koff = in.delay_off_ms;
ins.soundKeyOnMs = in.delay_on_ms;
ins.soundKeyOffMs = in.delay_off_ms;
}
template <class WOPNI>
static void cvt_FMIns_to_generic(WOPNI &ins, const opnInstMeta2 &in)
static void cvt_FMIns_to_generic(WOPNI &ins, const OpnInstMeta &in)
{
ins.percussion_key_number = in.tone;
ins.percussion_key_number = in.drumTone;
ins.inst_flags = in.flags;
ins.fbalg = in.opn[0].fbalg;
ins.lfosens = in.opn[0].lfosens;
ins.note_offset = in.opn[0].finetune;
ins.fbalg = in.op[0].fbalg;
ins.lfosens = in.op[0].lfosens;
ins.note_offset = in.op[0].noteOffset;
ins.midi_velocity_offset = in.midi_velocity_offset;
ins.midi_velocity_offset = in.midiVelocityOffset;
for(size_t op = 0; op < 4; op++)
{
ins.operators[op].dtfm_30 = in.opn[0].OPS[op].data[0];
ins.operators[op].level_40 = in.opn[0].OPS[op].data[1];
ins.operators[op].rsatk_50 = in.opn[0].OPS[op].data[2];
ins.operators[op].amdecay1_60 = in.opn[0].OPS[op].data[3];
ins.operators[op].decay2_70 = in.opn[0].OPS[op].data[4];
ins.operators[op].susrel_80 = in.opn[0].OPS[op].data[5];
ins.operators[op].ssgeg_90 = in.opn[0].OPS[op].data[6];
ins.operators[op].dtfm_30 = in.op[0].OPS[op].data[0];
ins.operators[op].level_40 = in.op[0].OPS[op].data[1];
ins.operators[op].rsatk_50 = in.op[0].OPS[op].data[2];
ins.operators[op].amdecay1_60 = in.op[0].OPS[op].data[3];
ins.operators[op].decay2_70 = in.op[0].OPS[op].data[4];
ins.operators[op].susrel_80 = in.op[0].OPS[op].data[5];
ins.operators[op].ssgeg_90 = in.op[0].OPS[op].data[6];
}
ins.delay_on_ms = in.ms_sound_kon;
ins.delay_off_ms = in.ms_sound_koff;
ins.delay_on_ms = in.soundKeyOnMs;
ins.delay_off_ms = in.soundKeyOffMs;
}

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2018 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -21,8 +21,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opnmidi_midiplay.hpp"
#include "opnmidi_opn2.hpp"
#include "opnmidi_private.hpp"
#include "opnmidi_cvt.hpp"
#include "file_reader.hpp"
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
#include "midi_sequencer.hpp"
#endif
#include "wopn/wopn_file.h"
bool OPNMIDIplay::LoadBank(const std::string &filename)
@ -39,12 +45,12 @@ bool OPNMIDIplay::LoadBank(const void *data, size_t size)
return LoadBank(file);
}
void cvt_OPNI_to_FMIns(opnInstMeta2 &ins, const OPN2_Instrument &in)
void cvt_OPNI_to_FMIns(OpnInstMeta &ins, const OPN2_Instrument &in)
{
return cvt_generic_to_FMIns(ins, in);
}
void cvt_FMIns_to_OPNI(OPN2_Instrument &ins, const opnInstMeta2 &in)
void cvt_FMIns_to_OPNI(OPN2_Instrument &ins, const OpnInstMeta &in)
{
cvt_FMIns_to_generic(ins, in);
}
@ -104,14 +110,17 @@ bool OPNMIDIplay::LoadBank(FileAndMemReader &fr)
}
}
m_synth.m_insBankSetup.volumeModel = wopn->volume_model;
m_synth.m_insBankSetup.lfoEnable = (wopn->lfo_freq & 8) != 0;
m_synth.m_insBankSetup.lfoFrequency = wopn->lfo_freq & 7;
Synth &synth = *m_synth;
synth.m_insBankSetup.volumeModel = wopn->volume_model;
synth.m_insBankSetup.lfoEnable = (wopn->lfo_freq & 8) != 0;
synth.m_insBankSetup.lfoFrequency = wopn->lfo_freq & 7;
synth.m_insBankSetup.chipType = wopn->chip_type;
m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO;
m_setup.lfoEnable = -1;
m_setup.lfoFrequency = -1;
m_setup.chipType = -1;
m_synth.m_insBanks.clear();
synth.m_insBanks.clear();
uint16_t slots_counts[2] = {wopn->banks_count_melodic, wopn->banks_count_percussion};
WOPNBank *slots_src_ins[2] = { wopn->banks_melodic, wopn->banks_percussive };
@ -122,12 +131,12 @@ bool OPNMIDIplay::LoadBank(FileAndMemReader &fr)
{
size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) +
(slots_src_ins[ss][i].bank_midi_lsb) +
(ss ? size_t(OPN2::PercussionTag) : 0);
OPN2::Bank &bank = m_synth.m_insBanks[bankno];
(ss ? size_t(Synth::PercussionTag) : 0);
Synth::Bank &bank = synth.m_insBanks[bankno];
for(int j = 0; j < 128; j++)
{
opnInstMeta2 &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(opnInstMeta2));
OpnInstMeta &ins = bank.ins[j];
std::memset(&ins, 0, sizeof(OpnInstMeta));
WOPNInstrument &inIns = slots_src_ins[ss][i].ins[j];
cvt_generic_to_FMIns(ins, inIns);
}
@ -145,7 +154,8 @@ bool OPNMIDIplay::LoadBank(FileAndMemReader &fr)
bool OPNMIDIplay::LoadMIDI_pre()
{
if(m_synth.m_insBanks.empty())
Synth &synth = *m_synth;
if(synth.m_insBanks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
@ -160,7 +170,9 @@ bool OPNMIDIplay::LoadMIDI_pre()
bool OPNMIDIplay::LoadMIDI_post()
{
MidiSequencer::FileFormat format = m_sequencer.getFormat();
Synth &synth = *m_synth;
MidiSequencer &seq = *m_sequencer;
MidiSequencer::FileFormat format = seq.getFormat();
if(format == MidiSequencer::Format_CMF)
{
errorStringOut = "OPNMIDI doesn't supports CMF, use ADLMIDI to play this file!";
@ -169,9 +181,9 @@ bool OPNMIDIplay::LoadMIDI_post()
}
else if(format == MidiSequencer::Format_RSXX)
{
m_synth.m_musicMode = OPN2::MODE_RSXX;
m_synth.m_volumeScale = OPN2::VOLUME_Generic;
m_synth.m_numChips = 2;
synth.m_musicMode = Synth::MODE_RSXX;
synth.m_volumeScale = Synth::VOLUME_Generic;
synth.m_numChips = 2;
}
else if(format == MidiSequencer::Format_IMF)
{
@ -179,11 +191,21 @@ bool OPNMIDIplay::LoadMIDI_post()
/* Same as for CMF */
return false;
}
else if(format == MidiSequencer::Format_XMIDI)
synth.m_musicMode = Synth::MODE_XMIDI;
m_setup.tick_skip_samples_delay = 0;
m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPN2 chip
synth.reset(m_setup.emulator, m_setup.PCM_RATE, synth.chipFamily(), this); // Reset OPN2 chip
m_chipChannels.clear();
m_chipChannels.resize(m_synth.m_numChannels);
m_chipChannels.resize(synth.m_numChannels);
resetMIDIDefaults();
#ifdef OPNMIDI_MIDI2VGM
m_sequencerInterface->onloopStart = synth.m_loopStartHook;
m_sequencerInterface->onloopStart_userData = synth.m_loopStartHookData;
m_sequencerInterface->onloopEnd = synth.m_loopEndHook;
m_sequencerInterface->onloopEnd_userData = synth.m_loopEndHookData;
m_sequencer->setLoopHooksOnly(m_sequencerInterface->onloopStart != NULL);
#endif
return true;
}
@ -195,9 +217,10 @@ bool OPNMIDIplay::LoadMIDI(const std::string &filename)
file.openFile(filename.c_str());
if(!LoadMIDI_pre())
return false;
if(!m_sequencer.loadMIDI(file))
MidiSequencer &seq = *m_sequencer;
if(!seq.loadMIDI(file))
{
errorStringOut = m_sequencer.getErrorString();
errorStringOut = seq.getErrorString();
return false;
}
if(!LoadMIDI_post())
@ -211,9 +234,10 @@ bool OPNMIDIplay::LoadMIDI(const void *data, size_t size)
file.openData(data, size);
if(!LoadMIDI_pre())
return false;
if(!m_sequencer.loadMIDI(file))
MidiSequencer &seq = *m_sequencer;
if(!seq.loadMIDI(file))
{
errorStringOut = m_sequencer.getErrorString();
errorStringOut = seq.getErrorString();
return false;
}
if(!LoadMIDI_post())

File diff suppressed because it is too large Load diff

1002
thirdparty/opnmidi/opnmidi_midiplay.hpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2018 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -21,10 +21,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opnmidi_opn2.hpp"
#include "opnmidi_private.hpp"
#if defined(OPNMIDI_DISABLE_NUKED_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_EMULATOR) && \
defined(OPNMIDI_DISABLE_GENS_EMULATOR) && defined(OPNMIDI_DISABLE_GX_EMULATOR)
defined(OPNMIDI_DISABLE_GENS_EMULATOR) && defined(OPNMIDI_DISABLE_GX_EMULATOR) && \
defined(OPNMIDI_DISABLE_NP2_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_2608_EMULATOR) && \
defined(OPNMIDI_DISABLE_PMDWIN_EMULATOR)
#error "No emulators enabled. You must enable at least one emulator to use this library!"
#endif
@ -48,6 +51,26 @@
#include "chips/gx_opn2.h"
#endif
// Neko Project II OPNA emulator
#ifndef OPNMIDI_DISABLE_NP2_EMULATOR
#include "chips/np2_opna.h"
#endif
// MAME YM2608 emulator
#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR
#include "chips/mame_opna.h"
#endif
// PMDWin OPNA emulator
#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR
#include "chips/pmdwin_opna.h"
#endif
// VGM File dumper
#ifdef OPNMIDI_MIDI2VGM
#include "chips/vgm_file_dumper.h"
#endif
static const unsigned opn2_emulatorSupport = 0
#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR
| (1u << OPNMIDI_EMU_NUKED)
@ -61,6 +84,18 @@ static const unsigned opn2_emulatorSupport = 0
#ifndef OPNMIDI_DISABLE_GX_EMULATOR
| (1u << OPNMIDI_EMU_GX)
#endif
#ifndef OPNMIDI_DISABLE_NP2_EMULATOR
| (1u << OPNMIDI_EMU_NP2)
#endif
#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR
| (1u << OPNMIDI_EMU_MAME_2608)
#endif
#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR
| (1u << OPNMIDI_EMU_PMDWIN)
#endif
#ifdef OPNMIDI_MIDI2VGM
| (1u << OPNMIDI_VGM_DUMPER)
#endif
;
//! Check emulator availability
@ -91,6 +126,42 @@ int opn2_getLowestEmulator()
return emu;
}
/***************************************************************
* Volume model tables *
***************************************************************/
// Mapping from MIDI volume level to OPL level value.
static const uint_fast32_t s_dmx_volume_model[128] =
{
0, 1, 3, 5, 6, 8, 10, 11,
13, 14, 16, 17, 19, 20, 22, 23,
25, 26, 27, 29, 30, 32, 33, 34,
36, 37, 39, 41, 43, 45, 47, 49,
50, 52, 54, 55, 57, 59, 60, 61,
63, 64, 66, 67, 68, 69, 71, 72,
73, 74, 75, 76, 77, 79, 80, 81,
82, 83, 84, 84, 85, 86, 87, 88,
89, 90, 91, 92, 92, 93, 94, 95,
96, 96, 97, 98, 99, 99, 100, 101,
101, 102, 103, 103, 104, 105, 105, 106,
107, 107, 108, 109, 109, 110, 110, 111,
112, 112, 113, 113, 114, 114, 115, 115,
116, 117, 117, 118, 118, 119, 119, 120,
120, 121, 121, 122, 122, 123, 123, 123,
124, 124, 125, 125, 126, 126, 127, 127,
};
static const uint_fast32_t W9X_volume_mapping_table[32] =
{
63, 63, 40, 36, 32, 28, 23, 21,
19, 17, 15, 14, 13, 12, 11, 10,
9, 8, 7, 6, 5, 5, 4, 4,
3, 3, 2, 2, 1, 1, 0, 0
};
static const uint32_t g_noteChannelsMap[6] = { 0, 1, 2, 4, 5, 6 };
static inline void getOpnChannel(size_t in_channel,
@ -104,15 +175,39 @@ static inline void getOpnChannel(size_t in_channel,
out_ch = static_cast<uint32_t>(ch4 % 3);
}
static opnInstMeta2 makeEmptyInstrument()
/***************************************************************
* Standard frequency formula *
* *************************************************************/
static inline double s_commonFreq(double tone)
{
opnInstMeta2 ins;
memset(&ins, 0, sizeof(opnInstMeta2));
ins.flags = opnInstMeta::Flag_NoSound;
return std::exp(0.057762265 * tone);
}
enum
{
MasterVolumeDefault = 127
};
enum
{
OPN_PANNING_LEFT = 0x80,
OPN_PANNING_RIGHT = 0x40,
OPN_PANNING_BOTH = 0xC0
};
static OpnInstMeta makeEmptyInstrument()
{
OpnInstMeta ins;
memset(&ins, 0, sizeof(OpnInstMeta));
ins.flags = OpnInstMeta::Flag_NoSound;
return ins;
}
const opnInstMeta2 OPN2::m_emptyInstrument = makeEmptyInstrument();
const OpnInstMeta OPN2::m_emptyInstrument = makeEmptyInstrument();
OPN2::OPN2() :
m_regLFOSetup(0),
@ -120,14 +215,17 @@ OPN2::OPN2() :
m_scaleModulators(false),
m_runAtPcmRate(false),
m_softPanning(false),
m_masterVolume(MasterVolumeDefault),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic),
m_lfoEnable(false),
m_lfoFrequency(0)
m_lfoFrequency(0),
m_chipFamily(OPNChip_OPN2)
{
m_insBankSetup.volumeModel = OPN2::VOLUME_Generic;
m_insBankSetup.lfoEnable = false;
m_insBankSetup.lfoFrequency = 0;
m_insBankSetup.chipType = OPNChip_OPN2;
// Initialize blank instruments banks
m_insBanks.clear();
@ -170,19 +268,32 @@ void OPN2::noteOff(size_t c)
writeRegI(chip, 0, 0x28, g_noteChannelsMap[ch4]);
}
void OPN2::noteOn(size_t c, double hertz) // Hertz range: 0..131071
void OPN2::noteOn(size_t c, double tone)
{
// Hertz range: 0..131071
double hertz = s_commonFreq(tone);
if(hertz < 0) // Avoid infinite loop
return;
double coef;
switch(m_chipFamily)
{
case OPNChip_OPN2: default:
coef = 321.88557; break;
case OPNChip_OPNA:
coef = 309.12412; break;
}
hertz *= coef;
size_t chip;
uint8_t port;
uint32_t cc;
size_t ch4 = c % 6;
getOpnChannel(c, chip, port, cc);
if(hertz < 0) // Avoid infinite loop
return;
uint32_t octave = 0, ftone = 0, mul_offset = 0;
const opnInstData &adli = m_insCache[c];
const OpnTimbre &adli = m_insCache[c];
//Basic range until max of octaves reaching
while((hertz >= 1023.75) && (octave < 0x3800))
@ -224,16 +335,20 @@ void OPN2::noteOn(size_t c, double hertz) // Hertz range: 0..131071
writeRegI(chip, 0, 0x28, 0xF0 + g_noteChannelsMap[ch4]);
}
void OPN2::touchNote(size_t c, uint8_t volume, uint8_t brightness)
void OPN2::touchNote(size_t c,
uint_fast32_t velocity,
uint_fast32_t channelVolume,
uint_fast32_t channelExpression,
uint8_t brightness)
{
if(volume > 127) volume = 127;
size_t chip;
uint8_t port;
uint32_t cc;
getOpnChannel(c, chip, port, cc);
const opnInstData &adli = m_insCache[c];
const OpnTimbre &adli = m_insCache[c];
uint_fast32_t volume = 0;
uint8_t op_vol[4] =
{
@ -261,12 +376,87 @@ void OPN2::touchNote(size_t c, uint8_t volume, uint8_t brightness)
{true ,true ,true ,true},//Algorithm #7: W = 1 + 2 + 3 + 4
};
switch(m_volumeScale)
{
default:
case Synth::VOLUME_Generic:
{
volume = velocity * m_masterVolume *
channelVolume * channelExpression;
/* If the channel has arpeggio, the effective volume of
* *this* instrument is actually lower due to timesharing.
* To compensate, add extra volume that corresponds to the
* time this note is *not* heard.
* Empirical tests however show that a full equal-proportion
* increment sounds wrong. Therefore, using the square root.
*/
//volume = (int)(volume * std::sqrt( (double) ch[c].users.size() ));
const double c1 = 11.541560327111707;
const double c2 = 1.601379199767093e+02;
const uint_fast32_t minVolume = 1108075; // 8725 * 127
// The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A)
if(volume > minVolume)
{
double lv = std::log(static_cast<double>(volume));
volume = static_cast<uint_fast32_t>(lv * c1 - c2) * 2.0;
}
else
volume = 0;
}
break;
case Synth::VOLUME_NATIVE:
{
volume = velocity * channelVolume * channelExpression;
//volume = volume * m_masterVolume / (127 * 127 * 127) / 2;
volume = (volume * m_masterVolume) / 4096766;
}
break;
case Synth::VOLUME_DMX:
{
volume = (channelVolume * channelExpression * m_masterVolume) / 16129;
volume = (s_dmx_volume_model[volume] + 1) << 1;
volume = (s_dmx_volume_model[(velocity < 128) ? velocity : 127] * volume) >> 9;
if(volume > 0)
volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64.
}
break;
case Synth::VOLUME_APOGEE:
{
volume = (channelVolume * channelExpression * m_masterVolume / 16129);
volume = ((64 * (velocity + 0x80)) * volume) >> 15;
//volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15;
if(volume > 0)
volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64.
}
break;
case Synth::VOLUME_9X:
{
//volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * 127 / 16129 /*2048383*/) >> 2)];
volume = 63 - W9X_volume_mapping_table[((velocity * channelVolume * channelExpression * m_masterVolume / 2048383) >> 2)];
//volume = W9X_volume_mapping_table[vol >> 2] + volume;
if(volume > 0)
volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64.
}
break;
}
if(volume > 127)
volume = 127;
uint8_t alg = adli.fbalg & 0x07;
for(uint8_t op = 0; op < 4; op++)
{
bool do_op = alg_do[alg][op] || m_scaleModulators;
uint32_t x = op_vol[op];
uint32_t vol_res = do_op ? (127 - (static_cast<uint32_t>(volume) * (127 - (x & 127)))/127) : x;
uint32_t vol_res = do_op ? (127 - (static_cast<uint32_t>(volume) * (127 - (x & 127))) / 127) : x;
if(brightness != 127)
{
brightness = static_cast<uint32_t>(::round(127.0 * ::sqrt((static_cast<double>(brightness)) * (1.0 / 127.0))));
@ -283,7 +473,7 @@ void OPN2::touchNote(size_t c, uint8_t volume, uint8_t brightness)
// 63 + chanvol * (instrvol / 63.0 - 1)
}
void OPN2::setPatch(size_t c, const opnInstData &instrument)
void OPN2::setPatch(size_t c, const OpnTimbre &instrument)
{
size_t chip;
uint8_t port;
@ -307,7 +497,7 @@ void OPN2::setPan(size_t c, uint8_t value)
uint8_t port;
uint32_t cc;
getOpnChannel(c, chip, port, cc);
const opnInstData &adli = m_insCache[c];
const OpnTimbre &adli = m_insCache[c];
uint8_t val = 0;
if(m_softPanning)
{
@ -398,7 +588,7 @@ void OPN2::clearChips()
m_chips.clear();
}
void OPN2::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *audioTickHandler)
{
#if !defined(ADLMIDI_AUDIO_TICK_HANDLER)
ADL_UNUSED(audioTickHandler);
@ -406,8 +596,19 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
clearChips();
m_insCache.clear();
m_regLFOSens.clear();
#ifdef OPNMIDI_MIDI2VGM
if(emulator == OPNMIDI_VGM_DUMPER && (m_numChips > 2))
m_numChips = 2;// VGM Dumper can't work in multichip mode
#endif
m_chips.resize(m_numChips, AdlMIDI_SPtr<OPNChipBase>());
#ifdef OPNMIDI_MIDI2VGM
m_loopStartHook = NULL;
m_loopStartHookData = NULL;
m_loopEndHook = NULL;
m_loopEndHookData = NULL;
#endif
for(size_t i = 0; i < m_chips.size(); i++)
{
OPNChipBase *chip;
@ -419,37 +620,66 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
abort();
#ifndef OPNMIDI_DISABLE_MAME_EMULATOR
case OPNMIDI_EMU_MAME:
chip = new MameOPN2;
chip = new MameOPN2(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR
case OPNMIDI_EMU_NUKED:
chip = new NukedOPN2;
chip = new NukedOPN2(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_GENS_EMULATOR
case OPNMIDI_EMU_GENS:
chip = new GensOPN2;
chip = new GensOPN2(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_GX_EMULATOR
case OPNMIDI_EMU_GX:
chip = new GXOPN2;
chip = new GXOPN2(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_NP2_EMULATOR
case OPNMIDI_EMU_NP2:
chip = new NP2OPNA<>(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR
case OPNMIDI_EMU_MAME_2608:
chip = new MameOPNA(family);
break;
#endif
#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR
case OPNMIDI_EMU_PMDWIN:
chip = new PMDWinOPNA(family);
break;
#endif
#ifdef OPNMIDI_MIDI2VGM
case OPNMIDI_VGM_DUMPER:
chip = new VGMFileDumper(family);
if(i == 0)//Set hooks for first chip only
{
m_loopStartHook = &VGMFileDumper::loopStartHook;
m_loopStartHookData = chip;
m_loopEndHook = &VGMFileDumper::loopEndHook;
m_loopEndHookData = chip;
}
break;
#endif
}
m_chips[i].reset(chip);
chip->setChipId((uint32_t)i);
chip->setRate((uint32_t)PCM_RATE, 7670454);
chip->setChipId(static_cast<uint32_t>(i));
chip->setRate(static_cast<uint32_t>(PCM_RATE), chip->nativeClockRate());
if(m_runAtPcmRate)
chip->setRunningAtPcmRate(true);
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
chip->setAudioTickHandlerInstance(audioTickHandler);
#endif
family = chip->family();
}
m_chipFamily = family;
m_numChannels = m_numChips * 6;
m_insCache.resize(m_numChannels, m_emptyInstrument.opn[0]);
m_insCache.resize(m_numChannels, m_emptyInstrument.op[0]);
m_regLFOSens.resize(m_numChannels, 0);
uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7);
@ -470,4 +700,13 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler)
}
silenceAll();
#ifdef OPNMIDI_MIDI2VGM
if(m_loopStartHook) // Post-initialization Loop Start hook (fix for loop edge passing clicks)
m_loopStartHook(m_loopStartHookData);
#endif
}
OPNFamily OPN2::chipFamily() const
{
return m_chipFamily;
}

286
thirdparty/opnmidi/opnmidi_opn2.hpp vendored Normal file
View file

@ -0,0 +1,286 @@
/*
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPNMIDI_OPN2_HPP
#define OPNMIDI_OPN2_HPP
#include "opnbank.h"
#include "opnmidi_ptr.hpp"
#include "opnmidi_private.hpp"
#include "opnmidi_bankmap.h"
#include "chips/opn_chip_family.h"
/**
* @brief OPN2 Chip management class
*/
class OPN2
{
friend class OPNMIDIplay;
public:
enum { PercussionTag = 1 << 15 };
//! Total number of chip channels between all running emulators
uint32_t m_numChannels;
//! Just a padding. Reserved.
char _padding[4];
//! Running chip emulators
std::vector<AdlMIDI_SPtr<OPNChipBase > > m_chips;
#ifdef OPNMIDI_MIDI2VGM
//! Loop Start hook
void (*m_loopStartHook)(void*);
//! Loop Start hook data
void *m_loopStartHookData;
//! Loop End hook
void (*m_loopEndHook)(void*);
//! Loop End hook data
void *m_loopEndHookData;
#endif
private:
//! Cached patch data, needed by Touch()
std::vector<OpnTimbre> m_insCache;
//! Cached per-channel LFO sensitivity flags
std::vector<uint8_t> m_regLFOSens;
//! LFO setup registry cache
uint8_t m_regLFOSetup;
public:
/**
* @brief MIDI bank entry
*/
struct Bank
{
//! MIDI Bank instruments
OpnInstMeta ins[128];
};
typedef BasicBankMap<Bank> BankMap;
//! MIDI bank instruments data
BankMap m_insBanks;
//! MIDI bank-wide setup
OpnBankSetup m_insBankSetup;
public:
//! Blank instrument template
static const OpnInstMeta m_emptyInstrument;
//! Total number of running concurrent emulated chips
uint32_t m_numChips;
//! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too.
bool m_scaleModulators;
//! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates.
bool m_runAtPcmRate;
//! Enable soft panning
bool m_softPanning;
//! Master volume, controlled via SysEx (0...127)
uint8_t m_masterVolume;
//! Just a padding. Reserved.
char _padding2[3];
/**
* @brief Music playing mode
*/
enum MusicMode
{
//! MIDI mode
MODE_MIDI,
//! MIDI mode
MODE_XMIDI,
//! Id-Software Music mode
MODE_IMF,
//! Creative Music Files mode
MODE_CMF,
//! EA-MUS (a.k.a. RSXX) mode
MODE_RSXX
} m_musicMode;
/**
* @brief Volume models enum
*/
enum VolumesScale
{
//! Generic volume model (linearization of logarithmic scale)
VOLUME_Generic,
//! OPN2 native logarithmic scale
VOLUME_NATIVE,
//! DMX volume scale logarithmic table
VOLUME_DMX,
//! Apoge Sound System volume scaling model
VOLUME_APOGEE,
//! Windows 9x driver volume scale table
VOLUME_9X
} m_volumeScale;
//! Reserved
bool m_lfoEnable;
uint8_t m_lfoFrequency;
//! Category of the channel
/*! 1 = DAC, 0 = regular
*/
std::vector<char> m_channelCategory;
//! Chip family
OPNFamily m_chipFamily;
/**
* @brief C.O. Constructor
*/
OPN2();
/**
* @brief C.O. Destructor
*/
~OPN2();
/**
* @brief Checks are setup locked to be changed on the fly or not
* @return true when setup on the fly is locked
*/
bool setupLocked();
/**
* @brief Write data to OPN2 chip register
* @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored
* @param port Port of the chip to write
* @param index Register address to write
* @param value Value to write
*/
void writeReg(size_t chip, uint8_t port, uint8_t index, uint8_t value);
/**
* @brief Write data to OPN2 chip register
* @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored
* @param port Port of the chip to write
* @param index Register address to write
* @param value Value to write
*/
void writeRegI(size_t chip, uint8_t port, uint32_t index, uint32_t value);
/**
* @brief Write to soft panning control of OPN2 chip emulator
* @param chip Index of emulated chip.
* @param address Register of channel to write
* @param value Value to write
*/
void writePan(size_t chip, uint32_t index, uint32_t value);
/**
* @brief Off the note in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
*/
void noteOff(size_t c);
/**
* @brief On the note in specified chip channel with specified frequency of the tone
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param tone The tone to play (integer part - MIDI halftone, decimal part - relative bend offset)
*/
void noteOn(size_t c, double tone);
/**
* @brief Change setup of instrument in specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param volume Volume level (from 0 to 127)
* @param brightness CC74 Brightness level (from 0 to 127)
*/
void touchNote(size_t c,
uint_fast32_t velocity,
uint_fast32_t channelVolume = 127,
uint_fast32_t channelExpression = 127,
uint8_t brightness = 127);
/**
* @brief Set the instrument into specified chip channel
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param instrument Instrument data to set into the chip channel
*/
void setPatch(size_t c, const OpnTimbre &instrument);
/**
* @brief Set panpot position
* @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)])
* @param value 3-bit panpot value
*/
void setPan(size_t c, uint8_t value);
/**
* @brief Shut up all chip channels
*/
void silenceAll();
/**
* @brief commit LFO enable and frequency
*/
void commitLFOSetup();
/**
* @brief Set the volume scaling model
* @param volumeModel Type of volume scale model scale
*/
void setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel);
/**
* @brief Get the volume scaling model
*/
OPNMIDI_VolumeModels getVolumeScaleModel();
/**
* @brief Clean up all running emulated chip instances
*/
void clearChips();
/**
* @brief Reset chip properties and initialize them
* @param emulator Type of chip emulator
* @param PCM_RATE Output sample rate to generate on output
* @param audioTickHandler PCM-accurate clock hook
*/
void reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *audioTickHandler);
/**
* @brief Gets the family of current chips
* @return the chip family
*/
OPNFamily chipFamily() const;
};
/**
* @brief Check emulator availability
* @param emulator Emulator ID (Opn2_Emulator)
* @return true when emulator is available
*/
extern bool opn2_isEmulatorAvailable(int emulator);
/**
* @brief Find highest emulator
* @return The Opn2_Emulator enum value which contains ID of highest emulator
*/
extern int opn2_getHighestEmulator();
/**
* @brief Find lowest emulator
* @return The Opn2_Emulator enum value which contains ID of lowest emulator
*/
extern int opn2_getLowestEmulator();
#endif // OPNMIDI_OPN2_HPP

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2018 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -21,6 +21,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "opnmidi_midiplay.hpp"
#include "opnmidi_opn2.hpp"
#include "opnmidi_private.hpp"
std::string OPN2MIDI_ErrorString;

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/*
* libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation
* libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation
*
* MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@ -54,7 +54,7 @@ class AdlMIDI_UPtr
{
T *m_p;
public:
explicit AdlMIDI_UPtr(T *p)
explicit AdlMIDI_UPtr(T *p = NULL)
: m_p(p) {}
~AdlMIDI_UPtr()
{

View file

@ -0,0 +1,133 @@
// Copyright Jean Pierre Cimalando 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef PL_LIST_HPP
#define PL_LIST_HPP
#include <iterator>
#include <cstddef>
/*
pl_cell: the linked list cell
*/
template <class T>
struct pl_cell;
template <class T>
struct pl_basic_cell
{
pl_cell<T> *prev, *next;
};
template <class T>
struct pl_cell : pl_basic_cell<T>
{
T value;
};
/*
pl_iterator: the linked list iterator
*/
template <class Cell>
class pl_iterator
{
public:
typedef std::bidirectional_iterator_tag iterator_category;
typedef Cell value_type;
typedef Cell &reference;
typedef Cell *pointer;
typedef std::ptrdiff_t difference_type;
pl_iterator(Cell *cell = NULL);
bool is_end() const;
Cell &operator*() const;
Cell *operator->() const;
bool operator==(const pl_iterator &i) const;
bool operator!=(const pl_iterator &i) const;
pl_iterator &operator++();
pl_iterator operator++(int);
pl_iterator &operator--();
pl_iterator operator--(int);
private:
Cell *cell_;
};
/*
pl_list: the preallocated linked list
*/
template <class T>
class pl_list
{
public:
typedef pl_cell<T> value_type;
typedef value_type *pointer;
typedef value_type &reference;
typedef const value_type *const_pointer;
typedef const value_type &const_reference;
typedef pl_iterator< pl_cell<T> > iterator;
typedef pl_iterator< const pl_cell<T> > const_iterator;
pl_list(std::size_t capacity = 0);
~pl_list();
struct external_storage_policy {};
pl_list(pl_cell<T> *cells, std::size_t ncells, external_storage_policy);
pl_list(const pl_list &other);
pl_list &operator=(const pl_list &other);
std::size_t size() const;
std::size_t capacity() const;
bool empty() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
void clear();
pl_cell<T> &front();
const pl_cell<T> &front() const;
pl_cell<T> &back();
const pl_cell<T> &back() const;
iterator insert(iterator pos, const T &x);
iterator erase(iterator pos);
void push_front(const T &x);
void push_back(const T &x);
void pop_front();
void pop_back();
iterator find(const T &x);
const_iterator find(const T &x) const;
template <class Pred> iterator find_if(const Pred &p);
template <class Pred> const_iterator find_if(const Pred &p) const;
private:
// number of cells in the list
std::size_t size_;
// number of cells allocated
std::size_t capacity_;
// array of cells allocated
pl_cell<T> *cells_;
// pointer to the head cell
pl_cell<T> *first_;
// pointer to the next free cell
pl_cell<T> *free_;
// value-less cell which terminates the linked list
pl_basic_cell<T> endcell_;
// whether cell storage is allocated
bool cells_allocd_;
void initialize(std::size_t capacity, pl_cell<T> *extcells = NULL);
pl_cell<T> *allocate(pl_cell<T> *pos);
void deallocate(pl_cell<T> *cell);
};
#include "pl_list.tcc"
#endif // PL_LIST_HPP

View file

@ -0,0 +1,338 @@
// Copyright Jean Pierre Cimalando 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "pl_list.hpp"
template <class Cell>
pl_iterator<Cell>::pl_iterator(Cell *cell)
: cell_(cell)
{
}
template <class Cell>
bool pl_iterator<Cell>::is_end() const
{
return cell_->next == NULL;
}
template <class Cell>
Cell &pl_iterator<Cell>::operator*() const
{
return *cell_;
}
template <class Cell>
Cell *pl_iterator<Cell>::operator->() const
{
return cell_;
}
template <class T>
bool pl_iterator<T>::operator==(const pl_iterator &i) const
{
return cell_ == i.cell_;
}
template <class T>
bool pl_iterator<T>::operator!=(const pl_iterator &i) const
{
return cell_ != i.cell_;
}
template <class T>
pl_iterator<T> &pl_iterator<T>::operator++()
{
cell_ = cell_->next;
return *this;
}
template <class T>
pl_iterator<T> pl_iterator<T>::operator++(int)
{
pl_iterator i(cell_);
cell_ = cell_->next;
return i;
}
template <class T>
pl_iterator<T> &pl_iterator<T>::operator--()
{
cell_ = cell_->prev;
return *this;
}
template <class T>
pl_iterator<T> pl_iterator<T>::operator--(int)
{
pl_iterator i(cell_);
cell_ = cell_->prev;
return i;
}
template <class T>
pl_list<T>::pl_list(std::size_t capacity)
{
initialize(capacity);
}
template <class T>
pl_list<T>::~pl_list()
{
if (cells_allocd_)
delete[] cells_;
}
template <class T>
pl_list<T>::pl_list(pl_cell<T> *cells, std::size_t ncells, external_storage_policy)
{
initialize(ncells, cells);
}
template <class T>
pl_list<T>::pl_list(const pl_list &other)
{
initialize(other.capacity());
for(const_iterator i = other.end(), b = other.begin(); i-- != b;)
push_front(i->value);
}
template <class T>
pl_list<T> &pl_list<T>::operator=(const pl_list &other)
{
if(this != &other)
{
std::size_t size = other.size();
if(size > capacity())
{
pl_cell<T> *oldcells = cells_;
bool allocd = cells_allocd_;
initialize(other.capacity());
if (allocd)
delete[] oldcells;
}
clear();
for(const_iterator i = other.end(), b = other.begin(); i-- != b;)
push_front(i->value);
}
return *this;
}
template <class T>
std::size_t pl_list<T>::size() const
{
return size_;
}
template <class T>
std::size_t pl_list<T>::capacity() const
{
return capacity_;
}
template <class T>
bool pl_list<T>::empty() const
{
return size_ == 0;
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::begin()
{
return iterator(first_);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::end()
{
return iterator(reinterpret_cast<pl_cell<T> *>(&endcell_));
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::begin() const
{
return const_iterator(first_);
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::end() const
{
return const_iterator(reinterpret_cast<const pl_cell<T> *>(&endcell_));
}
template <class T>
void pl_list<T>::clear()
{
std::size_t capacity = capacity_;
pl_cell<T> *cells = cells_;
pl_cell<T> *endcell = &*end();
size_ = 0;
first_ = endcell;
free_ = cells;
endcell->prev = NULL;
for(std::size_t i = 0; i < capacity; ++i)
{
cells[i].prev = (i > 0) ? &cells[i - 1] : NULL;
cells[i].next = (i + 1 < capacity) ? &cells[i + 1] : NULL;
cells[i].value = T();
}
}
template <class T>
pl_cell<T> &pl_list<T>::front()
{
return *first_;
}
template <class T>
const pl_cell<T> &pl_list<T>::front() const
{
return *first_;
}
template <class T>
pl_cell<T> &pl_list<T>::back()
{
iterator i = end();
return *--i;
}
template <class T>
const pl_cell<T> &pl_list<T>::back() const
{
const_iterator i = end();
return *--i;
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::insert(iterator pos, const T &x)
{
pl_cell<T> *cell = allocate(&*pos);
if (!cell)
throw std::bad_alloc();
cell->value = x;
return iterator(cell);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::erase(iterator pos)
{
deallocate(&*(pos++));
return pos;
}
template <class T>
void pl_list<T>::push_front(const T &x)
{
insert(begin(), x);
}
template <class T>
void pl_list<T>::push_back(const T &x)
{
insert(end(), x);
}
template <class T>
void pl_list<T>::pop_front()
{
deallocate(first_);
}
template <class T>
void pl_list<T>::pop_back()
{
iterator i(&*end());
deallocate(&*--i);
}
template <class T>
typename pl_list<T>::iterator pl_list<T>::find(const T &x)
{
const_iterator i = const_cast<const pl_list<T> *>(this)->find(x);
return iterator(&const_cast<reference>(*i));
}
template <class T>
typename pl_list<T>::const_iterator pl_list<T>::find(const T &x) const
{
const_iterator e = end();
for (const_iterator i = begin(); i != e; ++i)
{
if(i->value == x)
return i;
}
return e;
}
template <class T>
template <class Pred>
typename pl_list<T>::iterator pl_list<T>::find_if(const Pred &p)
{
const_iterator i = const_cast<const pl_list<T> *>(this)->find_if(p);
return iterator(&const_cast<reference>(*i));
}
template <class T>
template <class Pred>
typename pl_list<T>::const_iterator pl_list<T>::find_if(const Pred &p) const
{
const_iterator e = end();
for (const_iterator i = begin(); i != e; ++i)
{
if(p(i->value))
return i;
}
return e;
}
template <class T>
void pl_list<T>::initialize(std::size_t capacity, pl_cell<T> *extcells)
{
cells_ = extcells ? extcells : new pl_cell<T>[capacity];
cells_allocd_ = extcells ? false : true;
capacity_ = capacity;
endcell_.next = NULL;
clear();
}
template <class T>
pl_cell<T> *pl_list<T>::allocate(pl_cell<T> *pos)
{
// remove free cells front
pl_cell<T> *cell = free_;
if(!cell)
return NULL;
free_ = cell->next;
if(free_)
free_->prev = NULL;
// insert at position
if (pos == first_)
first_ = cell;
cell->prev = pos->prev;
if (cell->prev)
cell->prev->next = cell;
cell->next = pos;
pos->prev = cell;
++size_;
return cell;
}
template <class T>
void pl_list<T>::deallocate(pl_cell<T> *cell)
{
if(cell->prev)
cell->prev->next = cell->next;
if(cell->next)
cell->next->prev = cell->prev;
if(cell == first_)
first_ = cell->next;
cell->prev = NULL;
cell->next = free_;
cell->value = T();
free_ = cell;
--size_;
}

View file

@ -1,7 +1,7 @@
/*
* Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup
*
* Copyright (c) 2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2018-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
@ -82,18 +82,30 @@ static void fromSint16BE(int16_t in, uint8_t *arr)
WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks)
{
WOPNFile *file = NULL;
if(melodic_banks == 0)
return NULL;
if(percussive_banks == 0)
return NULL;
file = (WOPNFile*)calloc(1, sizeof(WOPNFile));
WOPNFile *file = (WOPNFile*)calloc(1, sizeof(WOPNFile));
if(!file)
return NULL;
file->banks_count_melodic = melodic_banks;
file->banks_count_percussion = percussive_banks;
file->banks_melodic = (WOPNBank*)calloc(1, sizeof(WOPNBank) * melodic_banks );
file->banks_percussive = (WOPNBank*)calloc(1, sizeof(WOPNBank) * percussive_banks );
file->banks_count_melodic = (melodic_banks != 0) ? melodic_banks : 1;
file->banks_melodic = (WOPNBank*)calloc(file->banks_count_melodic, sizeof(WOPNBank));
if(melodic_banks == 0)
{
unsigned i;
for(i = 0; i < 128; ++i)
file->banks_melodic[0].ins[i].inst_flags = WOPN_Ins_IsBlank;
}
file->banks_count_percussion = (percussive_banks != 0) ? percussive_banks : 1;
file->banks_percussive = (WOPNBank*)calloc(file->banks_count_percussion, sizeof(WOPNBank));
if(percussive_banks == 0)
{
unsigned i;
for(i = 0; i < 128; ++i)
file->banks_percussive[0].ins[i].inst_flags = WOPN_Ins_IsBlank;
}
return file;
}
@ -115,6 +127,7 @@ int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2)
res &= (bank1->version == bank2->version);
res &= (bank1->lfo_freq == bank2->lfo_freq);
res &= (bank1->chip_type == bank2->chip_type);
res &= (bank1->volume_model == bank2->volume_model);
res &= (bank1->banks_count_melodic == bank2->banks_count_melodic);
res &= (bank1->banks_count_percussion == bank2->banks_count_percussion);
@ -285,7 +298,9 @@ WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error)
}
outFile->version = version;
outFile->lfo_freq = head[4];
outFile->lfo_freq = head[4] & 0xf;
if(version >= 2)
outFile->chip_type = (head[4] >> 4) & 1;
outFile->volume_model = 0;
}
@ -516,7 +531,9 @@ int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t v
if(length < 1)
return WOPN_ERR_UNEXPECTED_ENDING;
cursor[0] = file->lfo_freq;
cursor[0] = file->lfo_freq & 0xf;
if (version >= 2)
cursor[0] |= (file->chip_type & 1) << 4;
GO_FORWARD(1);
bankslots[0] = file->banks_melodic;

View file

@ -1,7 +1,7 @@
/*
* Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup
*
* Copyright (c) 2018 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2018-2020 Vitaly Novichkov <admin@wohlnet.ru>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
@ -40,6 +40,15 @@ typedef signed short int int16_t;
typedef unsigned short int uint16_t;
#endif
/* Type of chip for which a bank has been designed */
typedef enum WOPN_ChipType
{
/* The Yamaha OPN2 chip, alias YM2612 YM3438 */
WOPN_Chip_OPN2 = 0,
/* The Yamaha OPNA chip, alias YM2608 */
WOPN_Chip_OPNA = 1
} WOPN_ChipType;
/* Volume scaling model implemented in the libOPNMIDI */
typedef enum WOPN_VolumeModel
{
@ -154,6 +163,8 @@ typedef struct WOPNFile
uint16_t banks_count_percussion;
/* Chip global LFO enable flag and frequency register data */
uint8_t lfo_freq;
/* Chip type this bank is designed for */
uint8_t chip_type;
/* Reserved (Enum WOPN_VolumeModel) */
uint8_t volume_model;
/* dynamically allocated data Melodic banks array */
@ -199,7 +210,7 @@ extern WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error);
/**
* @brief Load WOPI instrument file from the memory.
* You must allocate OPNIFile structure by yourself and give the pointer to it.
* @param file Pointer to destinition OPNIFile structure to fill it with parsed data.
* @param file Pointer to destination OPNIFile structure to fill it with parsed data.
* @param mem Pointer to memory block contains raw WOPI instrument file data
* @param length Length of given memory block
* @return 0 if no errors occouped, or an error code of WOPN_ErrorCodes enumeration
@ -209,7 +220,7 @@ extern int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length);
/**
* @brief Calculate the size of the output memory block
* @param file Heap-allocated WOPN file data structure
* @param version Destinition version of the file
* @param version Destination version of the file
* @return Size of the raw WOPN file data
*/
extern size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version);
@ -217,7 +228,7 @@ extern size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version);
/**
* @brief Calculate the size of the output memory block
* @param file Pointer to WOPI file data structure
* @param version Destinition version of the file
* @param version Destination version of the file
* @return Size of the raw WOPI file data
*/
extern size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version);
@ -225,8 +236,8 @@ extern size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version);
/**
* @brief Write raw WOPN into given memory block
* @param file Heap-allocated WOPN file data structure
* @param dest_mem Destinition memory block pointer
* @param length Length of destinition memory block
* @param dest_mem Destination memory block pointer
* @param length Length of destination memory block
* @param version Wanted WOPN version
* @param force_gm Force GM set in saved bank file
* @return Error code or 0 on success
@ -236,8 +247,8 @@ extern int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uin
/**
* @brief Write raw WOPI into given memory block
* @param file Pointer to WOPI file data structure
* @param dest_mem Destinition memory block pointer
* @param length Length of destinition memory block
* @param dest_mem Destination memory block pointer
* @param length Length of destination memory block
* @param version Wanted WOPI version
* @return Error code or 0 on success
*/