Updated libOPNMIDI to 1.5.1

Changelog:
 * Added an ability to disable the automatical arpeggio
 * Updated the GENS chip emulator from the 2.10 into GS/II (thanks to @freq-mod for the help)
 * Added an ability to set number of loops
 * Added an ability to disable/enable playing of selected MIDI channels
 * Fixed memory damages and crashes while playing XMI files
 * Added the chip channels allocation mode option
 * Fixed the playback of multi-song XMI files
 * Added an ability to switch the XMI song on the fly

And also:
 * Fixed the work on big endian processors
 * Fixed ARM64 build on some platforms
 * Improved support of the EA-MUS files (Thanks to [dashodanger](https://github.com/dashodanger))
 * Fixed crash on attempt to change the volume of a blank note
This commit is contained in:
Wohlstand 2023-01-02 01:24:15 +03:00 committed by Christoph Oelckers
parent 5bd573478b
commit faa997b986
50 changed files with 18303 additions and 6398 deletions

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -22,7 +22,7 @@ PRIVATE
chips/mamefm/resampler.cpp
chips/mamefm/fm.cpp
chips/nuked_opn2.cpp
chips/gens/Ym2612_Emu.cpp
chips/gens/Ym2612.cpp
chips/gx_opn2.cpp
chips/pmdwin_opna.cpp
chips/nuked/ym3438.c

2065
thirdparty/opnmidi/chips/gens/Ym2612.cpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
/***************************************************************************
* libgens: Gens Emulation Library. *
* Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. *
* *
* Copyright (c) 1999-2002 by Stéphane Dallongeville *
* Copyright (c) 2003-2004 by Stéphane Akhoun *
* Copyright (c) 2008-2015 by David Korth *
* *
* 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 2 of the License, or (at your *
* option) 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, write to the Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef __LIBGENS_SOUND_YM2612_HPP__
#define __LIBGENS_SOUND_YM2612_HPP__
#include <stdint.h>
namespace LibGens {
class Ym2612Private;
class Ym2612
{
public:
Ym2612();
Ym2612(int clock, int rate);
~Ym2612();
protected:
friend class Ym2612Private;
Ym2612Private *const d;
private:
// Q_DISABLE_COPY() equivalent.
// TODO: Add LibGens-specific version of Q_DISABLE_COPY().
Ym2612(const Ym2612 &);
Ym2612 &operator=(const Ym2612 &);
public:
int reInit(int clock, int rate);
void reset(void);
uint8_t read(void) const;
int write(unsigned int address, uint8_t data);
void write_pan(int channel, int data);
void update(int32_t *bufL, int32_t *bufR, int length);
// Properties.
// TODO: Read-only for now.
bool enabled(void) const { return m_enabled; }
bool dacEnabled(void) const { return m_dacEnabled; }
bool improved(void) const { return m_improved; }
/** Gens-specific code. **/
void updateDacAndTimers(int32_t *bufL, int32_t *bufR, int length);
void specialUpdate(void);
int getReg(int regID) const;
// YM write length.
inline void addWriteLen(int len)
{ m_writeLen += len; }
inline void clearWriteLen(void)
{ m_writeLen = 0; }
// Reset buffer pointers.
void resetBufferPtrs(int32_t *bufPtrL, int32_t *bufPtrR);
protected:
// PSG write length. (for audio output)
int m_writeLen;
bool m_enabled; // YM2612 Enabled
bool m_dacEnabled; // DAC Enabled
bool m_improved; // YM2612 Improved
// YM buffer pointers.
// TODO: Figure out how to get rid of these!
int32_t *m_bufPtrL;
int32_t *m_bufPtrR;
};
/* end */
}
#endif /* __LIBGENS_SOUND_YM2612_HPP__ */

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
// YM2612 FM sound chip emulator interface
// Game_Music_Emu 0.6.0
#ifndef YM2612_EMU_H
#define YM2612_EMU_H
struct Ym2612_Impl;
class Ym2612_Emu {
Ym2612_Impl* impl;
public:
Ym2612_Emu() { impl = 0; }
~Ym2612_Emu();
// Set output sample rate and chip clock rates, in Hz. Returns non-zero
// if error.
const char* set_rate( double sample_rate, double clock_rate );
// Reset to power-up state
void reset();
// Mute voice n if bit n (1 << n) of mask is set
enum { channel_count = 6 };
void mute_voices( int mask );
// Write addr to register 0 then data to register 1
void write0( int addr, int data );
// Write addr to register 2 then data to register 3
void write1( int addr, int data );
// Write pan level channel data
void write_pan( int channel, int data );
// Run and add pair_count samples into current output buffer contents
typedef short sample_t;
enum { out_chan_count = 2 }; // stereo
void run( int pair_count, sample_t* out );
};
#endif

View file

@ -0,0 +1,328 @@
/***************************************************************************
* libgens: Gens Emulation Library. *
* Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. (Private class) *
* *
* Copyright (c) 1999-2002 by Stéphane Dallongeville *
* Copyright (c) 2003-2004 by Stéphane Akhoun *
* Copyright (c) 2008-2015 by David Korth *
* *
* 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 2 of the License, or (at your *
* option) 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, write to the Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef __LIBGENS_SOUND_YM2612_P_HPP__
#define __LIBGENS_SOUND_YM2612_P_HPP__
// C includes.
#include <math.h>
#ifndef PI
#define PI 3.14159265358979323846
#endif
namespace LibGens {
class Ym2612;
class Ym2612Private
{
public:
Ym2612Private(Ym2612 *q);
protected:
friend class Ym2612;
Ym2612 *const q;
private:
// Q_DISABLE_COPY() equivalent.
// TODO: Add LibGens-specific version of Q_DISABLE_COPY().
Ym2612Private(const Ym2612Private &);
Ym2612Private &operator=(const Ym2612Private &);
public:
/**
* Initialize the static tables.
*/
static void doStaticInit(void);
struct slot_t {
unsigned int *DT; // paramètre detune
int MUL; // paramètre "multiple de fréquence"
int TL; // Total Level = volume lorsque l'enveloppe est au plus haut
int TLL; // Total Level ajusted
int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa première phase de régression
int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
int KSR; // Key Scale Rate = cette valeur est calculée par rapport à la fréquence actuelle, elle va influer
// sur les différents paramètres de l'enveloppe comme l'attaque, le decay ... comme dans la réalité !
int SEG; // Type enveloppe SSG
unsigned int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR])
unsigned int *DR; // Decay Rate (table pointeur) = Taux pour la régression (DR[KSR])
unsigned int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR])
unsigned int *RR; // Release Rate (table pointeur) = Taux pour le relâchement (RR[KSR])
int Fcnt; // Frequency Count = compteur-fréquence pour déterminer l'amplitude actuelle (SIN[Finc >> 16])
int Finc; // frequency step = pas d'incrémentation du compteur-fréquence
// plus le pas est grand, plus la fréquence est aïgu (ou haute)
int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase
// de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
// en fonction de la valeur de cette variable, on va appeler une fonction permettant
// de mettre à jour l'enveloppe courante.
int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
int Einc; // Envelope step courant
int Ecmp; // Envelope counter limite pour la prochaine phase
int EincA; // Envelope step for Attack = pas d'incrémentation du compteur durant la phase d'attaque
// cette valeur est égal à AR[KSR]
int EincD; // Envelope step for Decay = pas d'incrémentation du compteur durant la phase de regression
// cette valeur est égal à DR[KSR]
int EincS; // Envelope step for Sustain = pas d'incrémentation du compteur durant la phase de maintenue
// cette valeur est égal à SR[KSR]
int EincR; // Envelope step for Release = pas d'incrémentation du compteur durant la phase de relâchement
// cette valeur est égal à RR[KSR]
int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entrée
// d'un autre ou carrement à la sortie de la voie
int INd; // input data of the slot = données en entrée du slot
int ChgEnM; // Change envelop mask.
int AMS; // AMS depth level of this SLOT = degré de modulation de l'amplitude par le LFO
int AMSon; // AMS enable flag = drapeau d'activation de l'AMS
};
struct channel_t {
int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back)
int Old_OUTd; // ancienne sortie de la voie (son brut)
int OUTd; // sortie de la voie (son brut)
int LEFT; // LEFT enable flag
int RIGHT; // RIGHT enable flag
int ALGO; // Algorythm = détermine les connections entre les opérateurs
int FB; // shift count of self feed back = degré de "Feed-Back" du SLOT 1 (il est son unique entrée)
int FMS; // Fréquency Modulation Sensitivity of channel = degré de modulation de la fréquence sur la voie par le LFO
int AMS; // Amplitude Modulation Sensitivity of channel = degré de modulation de l'amplitude sur la voie par le LFO
int FNUM[4]; // hauteur fréquence de la voie (+ 3 pour le mode spécial)
int FOCT[4]; // octave de la voie (+ 3 pour le mode spécial)
int KC[4]; // Key Code = valeur fonction de la fréquence (voir KSR pour les slots, KSR = KC >> KSR_S)
slot_t _SLOT[4]; // four slot.operators = les 4 slots de la voie
int FFlag; // Frequency step recalculation flag
int PANVolumeL; // Left PCM output channel volume
int PANVolumeR; // Right PCM output channel volume
};
struct state_t {
int Clock; // Horloge YM2612
int Rate; // Sample Rate (11025/22050/44100)
int TimerBase; // TimerBase calculation
int status; // YM2612 Status (timer overflow)
int OPNAadr; // addresse pour l'écriture dans l'OPN A (propre à l'émulateur)
int OPNBadr; // addresse pour l'écriture dans l'OPN B (propre à l'émulateur)
int LFOcnt; // LFO counter = compteur-fréquence pour le LFO
int LFOinc; // LFO step counter = pas d'incrémentation du compteur-fréquence du LFO
// plus le pas est grand, plus la fréquence est grande
int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter
int TimerAL;
int TimerAcnt; // timerA counter = valeur courante du Timer A
int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter
int TimerBL;
int TimerBcnt; // timerB counter = valeur courante du Timer B
int Mode; // Mode actuel des voie 3 et 6 (normal / spécial)
int DAC; // DAC enabled flag
int DACdata; // DAC data
int dummy; // MSVC++ enforces 8-byte alignment on doubles. This forces said alignment on gcc.
double Frequence; // Fréquence de base, se calcul par rapport à l'horlage et au sample rate
unsigned int Inter_Cnt; // Interpolation Counter
unsigned int Inter_Step; // Interpolation Step
channel_t CHANNEL[6]; // Les 6 voies du YM2612
uint8_t REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
// cela nous rend le débuggage plus facile
};
// YM2612 state.
state_t state;
// Change it if you need to do long update
static const int MAX_UPDATE_LENGTH = 2000;
// Gens always uses 16 bits sound (in 32 bits buffer) and do the convertion later if needed.
static const int OUTPUT_BITS = 16;
/** Various YM2612 definitions needed for table size. **/
/** TODO: Rework them to be more C++-esque? **/
// TODO: Signed or unsigned?
// SIN_LBITS <= 16
// LFO_HBITS <= 16
// (SIN_LBITS + SIN_HBITS) <= 26
// (ENV_LBITS + ENV_HBITS) <= 28
// (LFO_LBITS + LFO_HBITS) <= 28
// NOTE: Needs to be a #define due to conditional use in shifting.
#define SIN_HBITS 12 // Sinus phase counter int part
#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting)
#if (SIN_LBITS > 16)
#define SIN_LBITS 16 // Can't be greater than 16 bits
#endif
// NOTE: Needs to be a #define due to conditional use in shifting.
#define ENV_HBITS 12 // Env phase counter int part
#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting)
// NOTE: Needs to be a #define due to conditional use in shifting.
#define LFO_HBITS 10 // LFO phase counter int part
#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting)
static const int SIN_LENGTH = (1 << SIN_HBITS);
static const int ENV_LENGTH = (1 << ENV_HBITS);
static const int LFO_LENGTH = (1 << LFO_HBITS);
static const int TL_LENGTH = (ENV_LENGTH * 3); // Env + TL scaling + LFO
static const int SIN_MASK = (SIN_LENGTH - 1);
static const int ENV_MASK = (ENV_LENGTH - 1);
static const int LFO_MASK = (LFO_LENGTH - 1);
// TODO: Change to static const double.
#define ENV_STEP (96.0 / ENV_LENGTH) // ENV_MAX = 96 dB
static const int ENV_ATTACK = ((ENV_LENGTH * 0) << ENV_LBITS);
static const int ENV_DECAY = ((ENV_LENGTH * 1) << ENV_LBITS);
static const int ENV_END = ((ENV_LENGTH * 2) << ENV_LBITS);
static const int MAX_OUT_BITS = (SIN_HBITS + SIN_LBITS + 2); // Modulation = -4 <--> +4
static const int MAX_OUT = ((1 << MAX_OUT_BITS) - 1);
//Just for tests stuff...
//
//static const double COEF_MOD = 0.5;
//static const int MAX_OUT = ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD));
static const int OUT_BITS = (OUTPUT_BITS - 2);
static const int OUT_SHIFT = (MAX_OUT_BITS - OUT_BITS);
static /*const */int LIMIT_CH_OUT/* = ((int) (((1 << OUT_BITS) * 1.5) - 1))*/;
static int PG_CUT_OFF/* = ((int) (78.0 / ENV_STEP))*/;
static int ENV_CUT_OFF/* = ((int) (68.0 / ENV_STEP))*/;
static const int AR_RATE = 399128;
static const int DR_RATE = 5514396;
//static const int AR_RATE = 426136;
//static const int DR_RATE = (AR_RATE * 12);
static const int LFO_FMS_LBITS = 9; // FIXED (LFO_FMS_BASE gives somethink as 1)
static /*const*/ int LFO_FMS_BASE/* = ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))*/;
enum ADSR {
ATTACK = 0,
DECAY = 1,
SUSTAIN = 2,
RELEASE = 3,
ADSR_MAX
};
enum SlotID {
S0 = 0,
S1 = 2, // Stupid typo of the YM2612
S2 = 1,
S3 = 3,
SlotID_MAX
};
// Static tables.
static bool isInit; // True if the static tables have been initialized.
static int *SIN_TAB[SIN_LENGTH]; // SINUS TABLE (pointer on TL TABLE)
static int TL_TAB[TL_LENGTH * 2]; // TOTAL LEVEL TABLE (plus and minus)
static unsigned int ENV_TAB[2 * ENV_LENGTH * 8]; // ENV CURVE TABLE (attack & decay)
//static unsigned int ATTACK_TO_DECAY[ENV_LENGTH]; // Conversion from attack to decay phase
static unsigned int DECAY_TO_ATTACK[ENV_LENGTH]; // Conversion from decay to attack phase
// Member tables.
unsigned int FINC_TAB[2048]; // Frequency step table
// Rate tables.
// All of these are Member variables, except for NULL_RATE.
// (NULL_RATE consists of all zeroes.)
unsigned int AR_TAB[128]; // Attack rate table.
unsigned int DR_TAB[96]; // Decay rate table.
unsigned int DT_TAB[8][32]; // Detune table.
static unsigned int SL_TAB[16]; // Sustain level table. (STATIC)
static unsigned int NULL_RATE[32]; // Table for NULL rate. (STATIC)
// LFO tables. (Static class variables)
static int LFO_ENV_TAB[LFO_LENGTH]; // LFO AMS TABLE (adjusted for 11.8 dB)
static int LFO_FREQ_TAB[LFO_LENGTH]; // LFO FMS TABLE
// LFO temporary tables. (Member variables)
int LFO_ENV_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO AMS (adjusted for 11.8 dB)
int LFO_FREQ_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO FMS
// NOTE: INTER_TAB isn't used...
//int INTER_TAB[MAX_UPDATE_LENGTH]; // Interpolation table
// LFO step table.
unsigned int LFO_INC_TAB[8]; // LFO step table.
// Envelope function declarations.
static void Env_Attack_Next(slot_t *SL);
static void Env_Decay_Next(slot_t *SL);
static void Env_Substain_Next(slot_t *SL);
static void Env_Release_Next(slot_t *SL);
static void Env_NULL_Next(slot_t *SL);
typedef void (*Env_Event)(slot_t *SL);
static const Env_Event ENV_NEXT_EVENT[8];
// Default detune table.
// FD == F number
static const uint8_t DT_DEF_TAB[4][32];
static const uint8_t FKEY_TAB[16];
static const uint8_t LFO_AMS_TAB[4];
static const uint8_t LFO_FMS_TAB[8];
// Interpolation calculation.
int int_cnt;
/** Functions for calculating parameters. **/
static void CALC_FINC_SL(slot_t *SL, int finc, int kc);
void CALC_FINC_CH(channel_t *CH);
/** Functions for setting values. **/
static void KEY_ON(channel_t *CH, int nsl);
static void KEY_OFF(channel_t *CH, int nsl);
void CSM_Key_Control(void);
int SLOT_SET(int address, uint8_t data);
int CHANNEL_SET(int address, uint8_t data);
int YM_SET(int address, uint8_t data);
/** Update Channel templates. **/
template<int algo>
inline void T_Update_Chan(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
template<int algo>
inline void T_Update_Chan_LFO(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
template<int algo>
inline void T_Update_Chan_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
template<int algo>
inline void T_Update_Chan_LFO_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
void Update_Chan(int algo_type, channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
};
}
#endif /* __LIBGENS_SOUND_YM2612_P_HPP__ */

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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
@ -19,13 +19,21 @@
*/
#include "gens_opn2.h"
#include <cstdio>
#include <cstring>
#include <cassert>
#include "gens/Ym2612.hpp"
#include "gens/Ym2612_Emu.h"
#ifndef INT16_MIN
#define INT16_MIN (-0x7fff - 1)
#endif
#ifndef INT16_MAX
#define INT16_MAX 0x7fff
#endif
GensOPN2::GensOPN2(OPNFamily f)
: OPNChipBaseBufferedT(f),
chip(new Ym2612_Emu())
chip(new LibGens::Ym2612())
{
setRate(m_rate, m_clock);
}
@ -39,7 +47,7 @@ void GensOPN2::setRate(uint32_t rate, uint32_t clock)
{
OPNChipBaseBufferedT::setRate(rate, clock);
uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate();
chip->set_rate(chipRate, clock); // implies reset()
chip->reInit(clock, chipRate); // implies reset()
}
void GensOPN2::reset()
@ -50,13 +58,17 @@ void GensOPN2::reset()
void GensOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data)
{
LibGens::Ym2612 *chip = this->chip;
switch (port)
{
case 0:
chip->write0(addr, data);
chip->write(0, addr);
chip->write(1, data);
break;
case 1:
chip->write1(addr, data);
chip->write(2, addr);
chip->write(3, data);
break;
}
}
@ -68,11 +80,31 @@ void GensOPN2::writePan(uint16_t chan, uint8_t data)
void GensOPN2::nativeGenerateN(int16_t *output, size_t frames)
{
std::memset(output, 0, frames * sizeof(int16_t) * 2);
chip->run((int)frames, output);
enum { maxFrames = 256 };
assert(frames <= maxFrames);
LibGens::Ym2612 *chip = this->chip;
int32_t bufLR[2 * maxFrames] = {};
int32_t *bufL = &bufLR[0];
int32_t *bufR = &bufLR[maxFrames];
chip->resetBufferPtrs(bufL, bufR);
chip->addWriteLen((int)frames);
chip->specialUpdate();
//TODO
// chip->updateDacAndTimers(bufL, bufR, frames);
for (size_t i = 0; i < 2 * frames; ++i) {
int32_t sample = ((i & 1) ? bufR : bufL)[i / 2];
sample /= 4; // has too high volume, attenuation needed
sample = (sample < INT16_MIN) ? INT16_MIN : sample;
sample = (sample > INT16_MAX) ? INT16_MAX : sample;
output[i] = (int16_t)sample;
}
}
const char *GensOPN2::emulatorName()
{
return "GENS 2.10 OPN2";
return "GENS/GS II OPN2";
}

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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,10 +23,10 @@
#include "opn_chip_base.h"
class Ym2612_Emu;
namespace LibGens { class Ym2612; }
class GensOPN2 final : public OPNChipBaseBufferedT<GensOPN2>
{
Ym2612_Emu *chip;
LibGens::Ym2612 *chip;
public:
explicit GensOPN2(OPNFamily f);
~GensOPN2() override;

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2018-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2018-2022 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

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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

@ -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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
thirdparty/opnmidi/chips/np2/LICENSE vendored Normal file
View file

@ -0,0 +1,24 @@
【著作権、免責規定】
・本ソースコードは作者(cisc@retropc.net) が著作権を所有しています。
・本ソースコードはあるがままに提供されるものであり,
暗黙及び明示的な保証を一切含みません.
・本ソースコードを利用したこと,利用しなかったこと,
利用できなかったことに関して生じたあるいは生じると予測される
損害について,作者は一切責任を負いません.
・本ソースコードは,以下の制限を満たす限り自由に改変・組み込み・
配布・利用することができます.
1. 本ソフトの由来(作者, 著作権)を明記すること.
2. 配布する際にはフリーソフトとすること.
3. 改変したソースコードを配布する際は改変内容を明示すること.
4. ソースコードを配布する際にはこのテキストを一切改変せずに
そのまま添付すること.
・公開の際に作者への連絡を頂ければ幸いです.
・商用ソフト(シェアウェア含む) に本ソースコードの一部,または
全部を組み込む際には,事前に作者の合意を得る必要があります.

View file

@ -0,0 +1,13 @@
OPNA FMGen changelog since adding into libOPNMIDI
- Shift-Jis to UTF8
- compiler optimize flags
- Added soft-panning to simulate full-panning when chip itself can do 3-state panning only
- fixed warnings and Windows builds
- fixed some mistakes in the code
- fixed C++98 compatibility
- Attempted to fix the SSG-EG behavior
The detailed changelog can be seen here:
https://github.com/Wohlstand/libOPNMIDI/commits/master/src/chips/np2

View file

@ -911,7 +911,7 @@ int Channel4::Prepare()
pms = pmtable[op[0].type_][op[0].ms_ & 7];
int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0;
int lfo = op[0].ms_ & (op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_ ? 0x37 : 7) ? 2 : 0;
int lfo = op[0].ms_ & ((op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_) ? 0x37 : 7) ? 2 : 0;
return key | lfo;
}

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2018-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2018-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (c) 2017-2020 Vitaly Novichkov (Wohlstand)
* Copyright (c) 2017-2022 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
thirdparty/opnmidi/chips/pmdwin/LICENSE vendored Normal file
View file

@ -0,0 +1,22 @@
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.

View file

@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <stdint.h>
#include <stdarg.h>
#include <math.h>
#include <unistd.h>
#include <assert.h>
#include "op.h"
#include "psg.h"

View file

@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "op.h"
#include "psg.h"

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2020 Vitaly Novichkov (Wohlstand)
* Copyright (C) 2022 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

View file

@ -1,7 +1,7 @@
/*
* Interfaces over Yamaha OPN2 (YM2612) chip emulators
*
* Copyright (C) 2020 Vitaly Novichkov (Wohlstand)
* Copyright (C) 2022 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

View file

@ -1,7 +1,7 @@
/*
* FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block
*
* Copyright (c) 2015-2020 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2015-2022 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"),
@ -110,15 +110,15 @@ public:
/**
* @brief Open file from memory block
* @param mem Pointer to the memory block
* @param lenght Size of given block
* @param length Size of given block
*/
void openData(const void *mem, size_t lenght)
void openData(const void *mem, size_t length)
{
if(m_fp)
this->close();//Close previously opened file first!
m_fp = NULL;
m_mp = mem;
m_mp_size = lenght;
m_mp_size = length;
m_mp_tell = 0;
}

View file

@ -2,7 +2,7 @@
* 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-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2016-2022 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
@ -129,6 +129,7 @@ struct OpnBankSetup
int lfoEnable;
int lfoFrequency;
int chipType;
bool mt32defaults;
};
/**

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -79,14 +79,14 @@ OPNMIDI_EXPORT int opn2_setDeviceIdentifier(OPN2_MIDIPlayer *device, unsigned id
return 0;
}
OPNMIDI_EXPORT int opn2_setNumChips(OPN2_MIDIPlayer *device, int numCards)
OPNMIDI_EXPORT int opn2_setNumChips(OPN2_MIDIPlayer *device, int numChips)
{
if(device == NULL)
return -2;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_setup.numChips = static_cast<unsigned int>(numCards);
play->m_setup.numChips = static_cast<unsigned int>(numChips);
if(play->m_setup.numChips < 1 || play->m_setup.numChips > OPN_MAX_CHIPS)
{
play->setErrorString("number of chips may only be 1.." OPN_MAX_CHIPS_STR ".\n");
@ -385,6 +385,24 @@ OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device,
play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0);
}
OPNMIDI_EXPORT void opn2_setAutoArpeggio(OPN2_MIDIPlayer *device, int aaEn)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_setup.enableAutoArpeggio = (aaEn != 0);
}
OPNMIDI_EXPORT int opn2_getAutoArpeggio(OPN2_MIDIPlayer *device)
{
if(!device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_setup.enableAutoArpeggio ? 1 : 0;
}
OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
@ -399,6 +417,34 @@ OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn)
#endif
}
OPNMIDI_EXPORT void opn2_setLoopCount(OPN2_MIDIPlayer *device, int loopCount)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer->setLoopsCount(loopCount);
#else
ADL_UNUSED(device);
ADL_UNUSED(loopCount);
#endif
}
OPNMIDI_EXPORT void opn2_setLoopHooksOnly(OPN2_MIDIPlayer *device, int loopHooksOnly)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer->setLoopHooksOnly(loopHooksOnly);
#else
ADL_UNUSED(device);
ADL_UNUSED(loopHooksOnly);
#endif
}
OPNMIDI_EXPORT void opn2_setSoftPanEnabled(OPN2_MIDIPlayer *device, int softPanEn)
{
if(!device)
@ -426,7 +472,7 @@ OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, i
}
}
OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volumeModel)
OPNMIDI_EXPORT void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel)
{
if(!device)
return;
@ -443,6 +489,27 @@ OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volume
}
}
OPNMIDI_EXPORT void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
Synth &synth = *play->m_synth;
if(chanalloc < -1 || chanalloc >= OPNMIDI_ChanAlloc_Count)
chanalloc = OPNMIDI_ChanAlloc_AUTO;
synth.m_channelAlloc = static_cast<OPNMIDI_ChannelAlloc>(chanalloc);
}
OPNMIDI_EXPORT int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device)
{
if(!device)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return static_cast<int>(play->m_synth->m_channelAlloc);
}
OPNMIDI_EXPORT int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device)
{
if(!device)
@ -507,6 +574,36 @@ OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsig
return -1;
}
OPNMIDI_EXPORT void opn2_selectSongNum(struct OPN2_MIDIPlayer *device, int songNumber)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->m_sequencer->setSongNum(songNumber);
#else
ADL_UNUSED(device);
ADL_UNUSED(songNumber);
#endif
}
OPNMIDI_EXPORT int opn2_getSongsCount(struct OPN2_MIDIPlayer *device)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return 0;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
return play->m_sequencer->getSongsCount();
#else
ADL_UNUSED(device);
return 0;
#endif
}
OPNMIDI_EXPORT const char *opn2_emulatorName()
{
return "<opn2_emulatorName() is deprecated! Use opn2_chipEmulatorName() instead!>";
@ -880,6 +977,37 @@ OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN
#endif
}
/* Set loop start hook */
OPNMIDI_EXPORT void opn2_setLoopStartHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopStartHook, void *userData)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->hooks.onLoopStart = loopStartHook;
play->hooks.onLoopStart_userData = userData;
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
play->m_sequencerInterface->onloopStart = loopStartHook;
play->m_sequencerInterface->onloopStart_userData = userData;
#endif
}
/* Set loop end hook */
OPNMIDI_EXPORT void opn2_setLoopEndHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopEndHook, void *userData)
{
if(!device)
return;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
play->hooks.onLoopEnd = loopEndHook;
play->hooks.onLoopEnd_userData = userData;
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
play->m_sequencerInterface->onloopEnd = loopEndHook;
play->m_sequencerInterface->onloopEnd_userData = userData;
#endif
}
template <class Dst>
static void CopySamplesRaw(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src,
@ -1043,69 +1171,65 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount,
while(left > 0)
{
{//
if(setup.delay <= 0.0)
setup.delay = double(left / 2) / double(setup.PCM_RATE);
const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay;
if(hasSkipped)
{
size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay;
n_periodCountStereo = samples / 2;
}
else
{
setup.delay -= eat_delay;
setup.carry += double(setup.PCM_RATE) * eat_delay;
n_periodCountStereo = static_cast<ssize_t>(setup.carry);
setup.carry -= double(n_periodCountStereo);
}
const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay;
if(hasSkipped)
{
size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay;
n_periodCountStereo = samples / 2;
}
else
{
setup.delay -= eat_delay;
setup.carry += double(setup.PCM_RATE) * eat_delay;
n_periodCountStereo = static_cast<ssize_t>(setup.carry);
setup.carry -= double(n_periodCountStereo);
}
//if(setup.SkipForward > 0)
// setup.SkipForward -= 1;
//else
{
if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0))
break;//Stop to fetch samples at reaching the song end with disabled loop
//if(setup.SkipForward > 0)
// setup.SkipForward -= 1;
//else
{
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;
if(n_periodCountStereo > leftSamples)
{
setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2;
n_periodCountStereo = leftSamples;
}
//! Count of stereo samples
ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo;
//! Total count of samples
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//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]));
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
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)
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)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
}
if(hasSkipped)
ssize_t leftSamples = left / 2;
if(n_periodCountStereo > leftSamples)
{
setup.tick_skip_samples_delay -= n_periodCountStereo * 2;
hasSkipped = setup.tick_skip_samples_delay > 0;
setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2;
n_periodCountStereo = leftSamples;
}
else
setup.delay = player->Tick(eat_delay, setup.mindelay);
}//
//! Count of stereo samples
ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo;
//! Total count of samples
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//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]));
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
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)
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)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
}
if(hasSkipped)
{
setup.tick_skip_samples_delay -= n_periodCountStereo * 2;
hasSkipped = setup.tick_skip_samples_delay > 0;
}
else
setup.delay = player->Tick(eat_delay, setup.mindelay);
}
return static_cast<int>(gotten_len);
@ -1140,47 +1264,45 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl
while(left > 0)
{
{//
if(delay <= 0.0)
delay = double(left / 2) / double(setup.PCM_RATE);
const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay;
delay -= eat_delay;
setup.carry += double(setup.PCM_RATE) * eat_delay;
n_periodCountStereo = static_cast<ssize_t>(setup.carry);
setup.carry -= double(n_periodCountStereo);
if(delay <= 0.0)
delay = double(left / 2) / double(setup.PCM_RATE);
const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay;
delay -= eat_delay;
setup.carry += double(setup.PCM_RATE) * eat_delay;
n_periodCountStereo = static_cast<ssize_t>(setup.carry);
setup.carry -= double(n_periodCountStereo);
{
ssize_t leftSamples = left / 2;
if(n_periodCountStereo > leftSamples)
n_periodCountStereo = leftSamples;
//! Count of stereo samples
ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo;
//! Total count of samples
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//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]));
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo);
else/* if(n_periodCountStereo > 0)*/
{
ssize_t leftSamples = left / 2;
if(n_periodCountStereo > leftSamples)
n_periodCountStereo = leftSamples;
//! Count of stereo samples
ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo;
//! Total count of samples
ssize_t in_generatedPhys = in_generatedStereo * 2;
//! Unsigned total sample count
//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]));
Synth &synth = *player->m_synth;
unsigned int chips = synth.m_numChips;
if(chips == 1)
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)
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)
return 0;
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
/* Generate data from every chip and mix result */
for(size_t card = 0; card < chips; ++card)
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)
return 0;
player->TickIterators(eat_delay);
}//...
left -= (int)in_generatedPhys;
gotten_len += (in_generatedPhys) /* - setup.stored_samples*/;
}
player->TickIterators(eat_delay);
}
return static_cast<int>(gotten_len);
@ -1271,6 +1393,25 @@ OPNMIDI_EXPORT int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t t
#endif
}
OPNMIDI_EXPORT int opn2_setChannelEnabled(struct OPN2_MIDIPlayer *device, size_t channelNumber, int enabled)
{
#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER
if(!device)
return -1;
MidiPlayer *play = GET_MIDI_PLAYER(device);
assert(play);
MidiSequencer &seq = *play->m_sequencer;
if(!seq.setChannelEnabled(channelNumber, (bool)enabled))
return -1;
return 0;
#else
ADL_UNUSED(device);
ADL_UNUSED(channelNumber);
ADL_UNUSED(enabled);
return -1;
#endif
}
OPNMIDI_EXPORT void opn2_panic(struct OPN2_MIDIPlayer *device)
{
if(!device)

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -30,7 +30,7 @@ extern "C" {
#define OPNMIDI_VERSION_MAJOR 1
#define OPNMIDI_VERSION_MINOR 5
#define OPNMIDI_VERSION_PATCHLEVEL 0
#define OPNMIDI_VERSION_PATCHLEVEL 1
#define OPNMIDI_TOSTR_I(s) #s
#define OPNMIDI_TOSTR(s) OPNMIDI_TOSTR_I(s)
@ -123,7 +123,26 @@ enum OPNMIDI_VolumeModels
/*! Logarithmic volume scale, used in Apogee Sound System. */
OPNMIDI_VolumeModel_APOGEE,
/*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */
OPNMIDI_VolumeModel_9X
OPNMIDI_VolumeModel_9X,
/*! Count of available volume model modes */
OPNMIDI_VolumeModel_Count
};
/*!
* \brief Algorithms of channel allocation for new notes
*/
enum OPNMIDI_ChannelAlloc
{
/*! Automatical choise of the method according to the volume model and internal preferrences */
OPNMIDI_ChanAlloc_AUTO = -1,
/*! Take only channels that has expired sounding delay */
OPNMIDI_ChanAlloc_OffDelay,
/*! Take any first released channel with the same instrument */
OPNMIDI_ChanAlloc_SameInst,
/*! Take any first released channel */
OPNMIDI_ChanAlloc_AnyReleased,
/*! Count of available channel allocation modes */
OPNMIDI_ChanAlloc_Count
};
/**
@ -186,7 +205,7 @@ struct OPN2_MIDIPlayer
* @param numChips Count of virtual chips to emulate
* @return 0 on success, <0 when any error has occurred
*/
extern OPNMIDI_DECLSPEC int opn2_setNumChips(struct OPN2_MIDIPlayer *device, int numCards);
extern OPNMIDI_DECLSPEC int opn2_setNumChips(struct OPN2_MIDIPlayer *device, int numChips);
/**
* @brief Get current number of emulated chips
@ -416,6 +435,21 @@ extern OPNMIDI_DECLSPEC void opn2_setScaleModulators(struct OPN2_MIDIPlayer *dev
*/
extern OPNMIDI_DECLSPEC void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness);
/**
* @brief Enable(1) or Disable(0) the automatical arpeggio system
*
* @param device Instance of the library
* @param aaEn 0 - disabled, 1 - enabled
*/
extern OPNMIDI_DECLSPEC void opn2_setAutoArpeggio(struct OPN2_MIDIPlayer *device, int aaEn);
/**
* @brief Get the state of the automatical arpeggio system enable state
* @param device Instalce of the library
* @return 0 - disabled, 1 - enabled
*/
extern OPNMIDI_DECLSPEC int opn2_getAutoArpeggio(struct OPN2_MIDIPlayer *device);
/**
* @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)
* @param device Instance of the library
@ -423,6 +457,23 @@ extern OPNMIDI_DECLSPEC void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer
*/
extern OPNMIDI_DECLSPEC void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn);
/**
* @brief Set how many times loop will be played
*
* Note: The song will be played once if loop has been disabled with no matter which value of loop count was set
*
* @param device Instance of the library
* @param loopCount Number of loops or -1 to loop infinitely
*/
extern OPNMIDI_DECLSPEC void opn2_setLoopCount(struct OPN2_MIDIPlayer *device, int loopCount);
/**
* @brief Make song immediately stop on reaching a loop end point
* @param device Instance of the library
* @param loopHooksOnly 0 - disabled, 1 - enabled
*/
extern OPNMIDI_DECLSPEC void opn2_setLoopHooksOnly(struct OPN2_MIDIPlayer *device, int loopHooksOnly);
/**
* @brief Enable or disable soft panning with chip emulators
* @param device Instance of the library
@ -452,10 +503,24 @@ extern OPNMIDI_DECLSPEC void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *de
*/
extern OPNMIDI_DECLSPEC int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device);
/**
* @brief Set the channel allocation mode
* @param device Instance of the library
* @param chanalloc Channel allocation mode (#OPNMIDI_ChannelAlloc)
*/
extern OPNMIDI_DECLSPEC void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc);
/**
* @brief Get the current channel allocation mode
* @param device Instance of the library
* @return Channel allocation mode (#OPNMIDI_ChannelAlloc)
*/
extern OPNMIDI_DECLSPEC int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device);
/**
* @brief Load WOPN bank file from File System
*
* Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time.
* Is recommended to call opn2_reset() to apply changes to already-loaded file player or real-time.
*
* @param device Instance of the library
* @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows.
@ -466,7 +531,7 @@ extern OPNMIDI_DECLSPEC int opn2_openBankFile(struct OPN2_MIDIPlayer *device, co
/**
* @brief Load WOPN bank file from memory data
*
* Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time.
* Is recommended to call opn2_reset() to apply changes to already-loaded file player or real-time.
*
* @param device Instance of the library
* @param mem Pointer to memory block where is raw data of WOPL bank file is stored
@ -483,7 +548,7 @@ extern OPNMIDI_DECLSPEC int opn2_openBankData(struct OPN2_MIDIPlayer *device, co
*
* @return A string that contains a notice to use `opn2_chipEmulatorName` instead of this function.
*/
OPNMIDI_DEPRECATED("Use `adl_chipEmulatorName(device)` instead")
OPNMIDI_DEPRECATED("Use `opn2_chipEmulatorName(device)` instead")
extern OPNMIDI_DECLSPEC const char *opn2_emulatorName();
/**
@ -601,7 +666,7 @@ extern OPNMIDI_DECLSPEC const char *opn2_errorInfo(struct OPN2_MIDIPlayer *devic
* 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.
* @return Instance of the library. If NULL was returned, check the `opn2_errorString` message for more info.
*/
extern OPNMIDI_DECLSPEC struct OPN2_MIDIPlayer *opn2_init(long sample_rate);
@ -637,6 +702,27 @@ extern OPNMIDI_DECLSPEC int opn2_openFile(struct OPN2_MIDIPlayer *device, const
*/
extern OPNMIDI_DECLSPEC int opn2_openData(struct OPN2_MIDIPlayer *device, const void *mem, unsigned long size);
/**
* @brief Switch another song if multi-song file is playing (for example, XMI)
*
* Note: to set the initial song to load, you should call this function
* BBEFORE calling `opn2_openFile` or `opn2_openData`. When loaded file has more than
* one built-in songs (Usually XMIformat), it will be started from the selected number.
* You may call this function to switch another song.
*
* @param device Instance of the library
* @param songNumber Identifier of the track to load (or -1 to mix all tracks as one song)
* @return
*/
extern OPNMIDI_DECLSPEC void opn2_selectSongNum(struct OPN2_MIDIPlayer *device, int songNumber);
/**
* @brief Retrive the number of songs in a currently opened file
* @param device Instance of the library
* @return Number of songs in the file. If 1 or less, means, the file has only one song inside.
*/
extern OPNMIDI_DECLSPEC int opn2_getSongsCount(struct OPN2_MIDIPlayer *device);
/**
* @brief Resets MIDI player (per-channel setup) into initial state
* @param device Instance of the library
@ -798,7 +884,7 @@ extern OPNMIDI_DECLSPEC struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDI
/**
* @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers
*
* Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData`
* Use this function when you are playing MIDI file loaded by `opn2_openFile` or by `opn2_openData`
* with using of built-in MIDI sequencer.
*
* Don't use count of frames, use instead count of samples. One frame is two samples.
@ -816,7 +902,7 @@ extern OPNMIDI_DECLSPEC int opn2_play(struct OPN2_MIDIPlayer *device, int sampl
/**
* @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers
*
* Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData`
* Use this function when you are playing MIDI file loaded by `opn2_openFile` or by `opn2_openData`
* with using of built-in MIDI sequencer.
*
* Don't use count of frames, use instead count of samples. One frame is two samples.
@ -906,6 +992,15 @@ enum OPNMIDI_TrackOptions
*/
extern OPNMIDI_DECLSPEC int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions);
/**
* @brief Sets the channel of the current sequence enable state
* @param device Instance of the library
* @param channelNumber Number of the channel (from 0 to 15)
* @param enabled 1 to enable and 0 to disable
* @return 0 on success, <0 when any error has occurred
*/
extern OPNMIDI_DECLSPEC int opn2_setChannelEnabled(struct OPN2_MIDIPlayer *device, size_t channelNumber, int enabled);
@ -1040,12 +1135,12 @@ typedef void (*OPN2_RawEventHook)(void *userdata, OPN2_UInt8 type, OPN2_UInt8 su
/**
* @brief Note on/off callback
* @param userdata Pointer to user data (usually, context of someting)
* @param adlchn Chip channel where note was played
* @param opnchn Chip channel where note was played
* @param note Note number [between 0 and 127]
* @param pressure Velocity level, or -1 when it's note off event
* @param bend Pitch bend offset value
*/
typedef void (*OPN2_NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend);
typedef void (*OPN2_NoteHook)(void *userdata, int opnchn, int note, int ins, int pressure, double bend);
/**
* @brief Debug messages callback
@ -1054,8 +1149,19 @@ typedef void (*OPN2_NoteHook)(void *userdata, int adlchn, int note, int ins, int
*/
typedef void (*OPN2_DebugMessageHook)(void *userdata, const char *fmt, ...);
/**
* @brief Loop start/end point reach hook
* @param userdata Pointer to user data (usually, context of someting)
*/
typedef void (*OPN2_LoopPointHook)(void *userdata);
/**
* @brief Set raw MIDI event hook
*
* CAUTION: Don't call any libOPNMIDI API functions from off this hook directly!
* Suggestion: Use boolean variables to mark the fact this hook got been called, and then,
* apply your action outside of this hook, for example, in the next after audio output call.
*
* @param device Instance of the library
* @param rawEventHook Pointer to the callback function which will be called on every MIDI event
* @param userData Pointer to user data which will be passed through the callback.
@ -1064,6 +1170,11 @@ extern OPNMIDI_DECLSPEC void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device
/**
* @brief Set note hook
*
* CAUTION: Don't call any libOPNMIDI API functions from off this hook directly!
* Suggestion: Use boolean variables to mark the fact this hook got been called, and then,
* apply your action outside of this hook, for example, in the next after audio output call.
*
* @param device Instance of the library
* @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event
* @param userData Pointer to user data which will be passed through the callback.
@ -1072,12 +1183,45 @@ extern OPNMIDI_DECLSPEC void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OP
/**
* @brief Set debug message hook
*
* CAUTION: Don't call any libOPNMIDI API functions from off this hook directly!
* Suggestion: Use boolean variables to mark the fact this hook got been called, and then,
* apply your action outside of this hook, for example, in the next after audio output call.
*
* @param device Instance of the library
* @param debugMessageHook Pointer to the callback function which will be called on every debug message
* @param userData Pointer to user data which will be passed through the callback.
*/
extern OPNMIDI_DECLSPEC void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN2_DebugMessageHook debugMessageHook, void *userData);
/**
* @brief Set the look start point hook
*
* CAUTION: Don't call any libOPNMIDI API functions from off this hook directly!
* Suggestion: Use boolean variables to mark the fact this hook got been called, and then,
* apply your action outside of this hook, for example, in the next after audio output call.
*
* @param device Instance of the library
* @param loopStartHook Pointer to the callback function which will be called on every loop start point passing
* @param userData Pointer to user data which will be passed through the callback.
*/
extern OPNMIDI_DECLSPEC void opn2_setLoopStartHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopStartHook, void *userData);
/**
* @brief Set the look start point hook
*
* CAUTION: Don't call any libOPNMIDI API functions from off this hook directly!
* Suggestion: Use boolean variables to mark the fact this hook got been called, and then,
* apply your action outside of this hook, for example, in the next after audio output call.
*
* If you want to switch the song after calling this hook, suggested to call the function
* opn2_setLoopHooksOnly(device, 1) to immediately stop the song on reaching the loop point
*
* @param device Instance of the library
* @param loopStartHook Pointer to the callback function which will be called on every loop start point passing
* @param userData Pointer to user data which will be passed through the callback.
*/
extern OPNMIDI_DECLSPEC void opn2_setLoopEndHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopEndHook, void *userData);
/**
* @brief Get a textual description of the channel state. For display only.

View file

@ -2,7 +2,7 @@
* 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-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2022 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

@ -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-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2022 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

@ -2,7 +2,7 @@
* 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-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2022 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

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -115,6 +115,8 @@ bool OPNMIDIplay::LoadBank(FileAndMemReader &fr)
synth.m_insBankSetup.lfoEnable = (wopn->lfo_freq & 8) != 0;
synth.m_insBankSetup.lfoFrequency = wopn->lfo_freq & 7;
synth.m_insBankSetup.chipType = wopn->chip_type;
// FIXME: Implement the bank-side flag to enable this
synth.m_insBankSetup.mt32defaults = false;
m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO;
m_setup.lfoEnable = -1;
m_setup.lfoFrequency = -1;

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -36,7 +36,8 @@ enum { MasterVolumeDefault = 127 };
inline bool isXgPercChannel(uint8_t msb, uint8_t lsb)
{
return (msb == 0x7E || msb == 0x7F) && (lsb == 0);
ADL_UNUSED(lsb);
return (msb == 0x7E || msb == 0x7F);
}
void OPNMIDIplay::OpnChannel::addAge(int64_t us)
@ -88,6 +89,7 @@ OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) :
//m_setup.SkipForward = 0;
m_setup.ScaleModulators = 0;
m_setup.fullRangeBrightnessCC74 = false;
m_setup.enableAutoArpeggio = false;
m_setup.delay = 0.0;
m_setup.carry = 0.0;
m_setup.tick_skip_samples_delay = 0;
@ -204,15 +206,15 @@ void OPNMIDIplay::resetMIDIDefaults(int offset)
for(size_t c = offset, n = m_midiChannels.size(); c < n; ++c)
{
MIDIchannel &ch = m_midiChannels[c];
if(synth.m_musicMode == Synth::MODE_XMIDI)
if(synth.m_musicMode == Synth::MODE_RSXX)
ch.def_volume = 127;
else if(synth.m_insBankSetup.mt32defaults)
{
ch.def_volume = 127;
ch.def_bendsense_lsb = 0;
ch.def_bendsense_msb = 12;
}
else
if(synth.m_musicMode == Synth::MODE_RSXX)
ch.def_volume = 127;
}
}
@ -294,10 +296,11 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit
if(!i.is_end())
{
MIDIchannel::NoteInfo &ni = i->value;
const int veloffset = ni.ains->midiVelocityOffset;
const int veloffset = ni.ains ? ni.ains->midiVelocityOffset : 0;
velocity = static_cast<uint8_t>(std::min(127, std::max(1, static_cast<int>(velocity) + veloffset)));
ni.vol = velocity;
noteUpdate(channel, i, Upd_Volume);
if(ni.ains)
noteUpdate(channel, i, Upd_Volume);
return false;
}
}
@ -340,7 +343,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit
// Let XG Percussion bank will use (0...127 LSB range in WOPN file)
// Choose: SFX or Drum Kits
bank = midiins + ((bank == 0x7E00) ? 128 : 0);
bank = midiins + ((midiChan.bank_msb == 0x7E) ? 128 : 0);
}
else
{
@ -356,31 +359,55 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit
//Set bank bank
const Synth::Bank *bnk = NULL;
if((bank & static_cast<size_t>(~static_cast<uint16_t>(Synth::PercussionTag))) > 0)
bool caughtMissingBank = false;
if((bank & ~static_cast<uint16_t>(Synth::PercussionTag)) > 0)
{
Synth::BankMap::iterator b = synth.m_insBanks.find(bank);
if(b != synth.m_insBanks.end())
bnk = &b->second;
if(bnk)
ains = &bnk->ins[midiins];
else if(hooks.onDebugMessage)
else
caughtMissingBank = true;
}
//Or fall back to bank ignoring LSB (GS/XG)
if(ains->flags & OpnInstMeta::Flag_NoSound)
{
size_t fallback = bank & ~(size_t)0x7F;
if(fallback != bank)
{
std::set<size_t> &missing = (isPercussion) ?
caugh_missing_banks_percussion : caugh_missing_banks_melodic;
const char *text = (isPercussion) ?
"percussion" : "melodic";
if(missing.insert(bank).second)
hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins);
Synth::BankMap::iterator b = synth.m_insBanks.find(fallback);
caughtMissingBank = false;
if(b != synth.m_insBanks.end())
bnk = &b->second;
if(bnk)
ains = &bnk->ins[midiins];
else
caughtMissingBank = true;
}
}
if(caughtMissingBank && hooks.onDebugMessage)
{
std::set<size_t> &missing = (isPercussion) ?
caugh_missing_banks_percussion : caugh_missing_banks_melodic;
const char *text = (isPercussion) ?
"percussion" : "melodic";
if(missing.insert(bank).second)
{
hooks.onDebugMessage(hooks.onDebugMessage_userData,
"[%i] Playing missing %s MIDI bank %i (patch %i)",
channel, text, (bank & ~static_cast<uint16_t>(Synth::PercussionTag)), midiins);
}
}
//Or fall back to first bank
if(ains->flags & OpnInstMeta::Flag_NoSound)
if((ains->flags & OpnInstMeta::Flag_NoSound) != 0)
{
Synth::BankMap::iterator b = synth.m_insBanks.find(bank & Synth::PercussionTag);
if(b != synth.m_insBanks.end())
bnk = &b->second;
if(bnk)
ains = &bnk->ins[midiins];
}
@ -408,13 +435,10 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit
if(ains->drumTone)
{
/*if(ains.tone < 20)
tone += ains.tone;
else*/
if(ains->drumTone < 128)
tone = ains->drumTone;
if(ains->drumTone >= 128)
tone = ains->drumTone - 128;
else
tone -= ains->drumTone - 128;
tone = ains->drumTone;
}
MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = {
@ -450,7 +474,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit
return false;
}
// Allocate AdLib channel (the physical sound channel for the note)
// Allocate OPN2 channel (the physical sound channel for the note)
int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 };
for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount)
@ -1251,14 +1275,39 @@ int64_t OPNMIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::N
const OpnChannel &chan = m_chipChannels[c];
int64_t koff_ms = chan.koff_time_until_neglible_us / 1000;
int64_t s = -koff_ms;
OPNMIDI_ChannelAlloc allocType = synth.m_channelAlloc;
if(allocType == OPNMIDI_ChanAlloc_AUTO)
{
if(synth.m_musicMode == Synth::MODE_CMF)
allocType = OPNMIDI_ChanAlloc_SameInst;
else
allocType = OPNMIDI_ChanAlloc_OffDelay;
}
// Rate channel with a releasing note
if(s < 0 && chan.users.empty())
{
bool isSame = (chan.recent_ins == ins);
s -= 40000;
// If it's same instrument, better chance to get it when no free channels
if(chan.recent_ins == ins)
s = (synth.m_musicMode == Synth::MODE_CMF) ? 0 : -koff_ms;
switch(allocType)
{
case OPNMIDI_ChanAlloc_SameInst:
if(isSame)
s = 0; // Re-use releasing channel with the same instrument
break;
case OPNMIDI_ChanAlloc_AnyReleased:
s = 0; // Re-use any releasing channel
break;
default:
case OPNMIDI_ChanAlloc_OffDelay:
if(isSame)
s = -koff_ms; // Wait until releasing sound will complete
break;
}
return s;
}
@ -1385,6 +1434,9 @@ void OPNMIDIplay::killOrEvacuate(size_t from_channel,
{
uint16_t cs = static_cast<uint16_t>(c);
if(!m_setup.enableAutoArpeggio)
break; // Arpeggio disabled completely
if(c >= maxChannels)
break;
if(c == from_channel)
@ -1617,9 +1669,16 @@ void OPNMIDIplay::updateArpeggio(double) // amount = amount of time passed
Synth &synth = *m_synth;
#if 0
if(!m_setup.enableAutoArpeggio) // Arpeggio was disabled
{
if(m_arpeggioCounter != 0)
m_arpeggioCounter = 0;
return;
}
#if 0
const unsigned desired_arpeggio_rate = 40; // Hz (upper limit)
#if 1
# if 1
static unsigned cache = 0;
amount = amount; // Ignore amount. Assume we get a constant rate.
cache += MaxSamplesAtTime * desired_arpeggio_rate;
@ -1627,15 +1686,15 @@ void OPNMIDIplay::updateArpeggio(double) // amount = amount of time passed
if(cache < PCM_RATE) return;
cache %= PCM_RATE;
#else
# else
static double arpeggio_cache = 0;
arpeggio_cache += amount * desired_arpeggio_rate;
if(arpeggio_cache < 1.0) return;
arpeggio_cache = 0.0;
#endif
#endif
# endif
#endif
++m_arpeggioCounter;

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -37,6 +37,10 @@ struct MIDIEventHooks
MIDIEventHooks() :
onNote(NULL),
onNote_userData(NULL),
onLoopStart(NULL),
onLoopStart_userData(NULL),
onLoopEnd(NULL),
onLoopEnd_userData(NULL),
onDebugMessage(NULL),
onDebugMessage_userData(NULL)
{}
@ -46,6 +50,12 @@ struct MIDIEventHooks
NoteHook onNote;
void *onNote_userData;
// Loop start/end hooks
OPN2_LoopPointHook onLoopStart;
void *onLoopStart_userData;
OPN2_LoopPointHook onLoopEnd;
void *onLoopEnd_userData;
//! Library internal debug messages
typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...);
DebugMessageHook onDebugMessage;
@ -521,6 +531,7 @@ public:
//unsigned int SkipForward;
int ScaleModulators;
bool fullRangeBrightnessCC74;
bool enableAutoArpeggio;
double delay;
double carry;

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -218,6 +218,7 @@ OPN2::OPN2() :
m_masterVolume(MasterVolumeDefault),
m_musicMode(MODE_MIDI),
m_volumeScale(VOLUME_Generic),
m_channelAlloc(OPNMIDI_ChanAlloc_AUTO),
m_lfoEnable(false),
m_lfoFrequency(0),
m_chipFamily(OPNChip_OPN2)
@ -226,6 +227,7 @@ OPN2::OPN2() :
m_insBankSetup.lfoEnable = false;
m_insBankSetup.lfoFrequency = 0;
m_insBankSetup.chipType = OPNChip_OPN2;
m_insBankSetup.mt32defaults = false;
// Initialize blank instruments banks
m_insBanks.clear();
@ -412,6 +414,9 @@ void OPN2::touchNote(size_t c,
volume = velocity * channelVolume * channelExpression;
//volume = volume * m_masterVolume / (127 * 127 * 127) / 2;
volume = (volume * m_masterVolume) / 4096766;
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;
@ -538,6 +543,7 @@ void OPN2::setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel)
{
switch(volumeModel)
{
default:
case OPNMIDI_VolumeModel_AUTO://Do nothing until restart playing
break;
@ -600,6 +606,7 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *a
if(emulator == OPNMIDI_VGM_DUMPER && (m_numChips > 2))
m_numChips = 2;// VGM Dumper can't work in multichip mode
#endif
m_chips.clear();
m_chips.resize(m_numChips, AdlMIDI_SPtr<OPNChipBase>());
#ifdef OPNMIDI_MIDI2VGM
@ -611,7 +618,7 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *a
for(size_t i = 0; i < m_chips.size(); i++)
{
OPNChipBase *chip;
OPNChipBase *chip = NULL;
switch(emulator)
{
@ -655,7 +662,7 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *a
#endif
#ifdef OPNMIDI_MIDI2VGM
case OPNMIDI_VGM_DUMPER:
chip = new VGMFileDumper(family);
chip = new VGMFileDumper(family, i, (i == 0 ? NULL : m_chips[0].get()));
if(i == 0)//Set hooks for first chip only
{
m_loopStartHook = &VGMFileDumper::loopStartHook;
@ -666,11 +673,14 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *a
break;
#endif
}
m_chips[i].reset(chip);
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
@ -685,18 +695,18 @@ void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *a
uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7);
m_regLFOSetup = regLFOSetup;
for(size_t card = 0; card < m_numChips; ++card)
for(size_t chip = 0; chip < m_numChips; ++chip)
{
writeReg(card, 0, 0x22, regLFOSetup);//push current LFO state
writeReg(card, 0, 0x27, 0x00); //set Channel 3 normal mode
writeReg(card, 0, 0x2B, 0x00); //Disable DAC
writeReg(chip, 0, 0x22, regLFOSetup);//push current LFO state
writeReg(chip, 0, 0x27, 0x00); //set Channel 3 normal mode
writeReg(chip, 0, 0x2B, 0x00); //Disable DAC
//Shut up all channels
writeReg(card, 0, 0x28, 0x00 ); //Note Off 0 channel
writeReg(card, 0, 0x28, 0x01 ); //Note Off 1 channel
writeReg(card, 0, 0x28, 0x02 ); //Note Off 2 channel
writeReg(card, 0, 0x28, 0x04 ); //Note Off 3 channel
writeReg(card, 0, 0x28, 0x05 ); //Note Off 4 channel
writeReg(card, 0, 0x28, 0x06 ); //Note Off 5 channel
writeReg(chip, 0, 0x28, 0x00); //Note Off 0 channel
writeReg(chip, 0, 0x28, 0x01); //Note Off 1 channel
writeReg(chip, 0, 0x28, 0x02); //Note Off 2 channel
writeReg(chip, 0, 0x28, 0x04); //Note Off 3 channel
writeReg(chip, 0, 0x28, 0x05); //Note Off 4 channel
writeReg(chip, 0, 0x28, 0x06); //Note Off 5 channel
}
silenceAll();

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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
@ -130,6 +130,9 @@ public:
VOLUME_9X
} m_volumeScale;
//! Channel allocation algorithm
OPNMIDI_ChannelAlloc m_channelAlloc;
//! Reserved
bool m_lfoEnable;
uint8_t m_lfoFrequency;

View file

@ -2,7 +2,7 @@
* 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>
* OPNMIDI Library and YM2612 support: Copyright (c) 2017-2022 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

@ -2,7 +2,7 @@
* libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
* ADLMIDI Library API: Copyright (c) 2017-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2017-2022 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

@ -2,7 +2,7 @@
* 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-2020 Vitaly Novichkov <admin@wohlnet.ru>
* ADLMIDI Library API: Copyright (c) 2015-2022 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,7 +1,7 @@
/*
* Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup
*
* Copyright (c) 2018-2020 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2018-2022 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"),

View file

@ -1,7 +1,7 @@
/*
* Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup
*
* Copyright (c) 2018-2020 Vitaly Novichkov <admin@wohlnet.ru>
* Copyright (c) 2018-2022 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"),
@ -32,8 +32,22 @@
extern "C" {
#endif
#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \
|| defined(__STRICT_ANSI__) || !defined(__cplusplus)
/* Solaris defines the integer types regardless of what C/C++ standard is actually available,
* so avoid defining them at all by ourselves. */
#if !defined(WOPN_STDINT_TYPEDEFS_NOT_NEEDED) && defined(__sun)
# define WOPN_STDINT_TYPEDEFS_NOT_NEEDED
#endif
#if !defined(WOPN_STDINT_TYPEDEFS_NEEDED) && !defined(WOPN_STDINT_TYPEDEFS_NOT_NEEDED)
# if !defined(__STDC_VERSION__) || \
(defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) || \
defined(__STRICT_ANSI__) || \
!defined(__cplusplus)
# define WOPN_STDINT_TYPEDEFS_NEEDED
# endif
#endif
#ifdef WOPN_STDINT_TYPEDEFS_NEEDED
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int int16_t;