MCUME/MCUME_pico/picosnd/StSnd/Ym2149Ex.cpp
2021-05-01 21:08:50 +02:00

579 lines
14 KiB
C++
Executable file

/*-----------------------------------------------------------------------------
ST-Sound ( YM files player library )
Extended YM-2149 Emulator, with ATARI music demos effects.
(SID-Like, Digidrum, Sync Buzzer, Sinus SID and Pattern SID)
-----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
* ST-Sound, ATARI-ST Music Emulator
* Copyright (c) 1995-1999 Arnaud Carre ( http://leonard.oxg.free.fr )
* 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 AUTHOR 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 AUTHOR 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.
*
-----------------------------------------------------------------------------*/
#include "YmTypes.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include "Ym2149Ex.h"
//-------------------------------------------------------------------
// env shapes.
//-------------------------------------------------------------------
static const ymint Env00xx[8]={ 1,0,0,0,0,0,0,0 };
static const ymint Env01xx[8]={ 0,1,0,0,0,0,0,0 };
static const ymint Env1000[8]={ 1,0,1,0,1,0,1,0 };
static const ymint Env1001[8]={ 1,0,0,0,0,0,0,0 };
static const ymint Env1010[8]={ 1,0,0,1,1,0,0,1 };
static const ymint Env1011[8]={ 1,0,1,1,1,1,1,1 };
static const ymint Env1100[8]={ 0,1,0,1,0,1,0,1 };
static const ymint Env1101[8]={ 0,1,1,1,1,1,1,1 };
static const ymint Env1110[8]={ 0,1,1,0,0,1,1,0 };
static const ymint Env1111[8]={ 0,1,0,0,0,0,0,0 };
static const ymint * EnvWave[16] = { Env00xx,Env00xx,Env00xx,Env00xx,
Env01xx,Env01xx,Env01xx,Env01xx,
Env1000,Env1001,Env1010,Env1011,
Env1100,Env1101,Env1110,Env1111};
static ymint ymVolumeTable[16] =
{ 62,161,265,377,580,774,1155,1575,2260,3088,4570,6233,9330,13187,21220,32767};
//----------------------------------------------------------------------
// Very cool and fast DC Adjuster ! This is the *new* stuff of that
// package coz I get that idea working on SainT in 2004 !
// ( almost everything here is from 1995 !)
//----------------------------------------------------------------------
CDcAdjuster::CDcAdjuster()
{
Reset();
}
void CDcAdjuster::Reset(void)
{
for (ymint i=0;i<DC_ADJUST_BUFFERLEN;i++)
m_buffer[i] = 0;
m_pos = 0;
m_sum = 0;
}
void CDcAdjuster::AddSample(ymint sample)
{
m_sum -= m_buffer[m_pos];
m_sum += sample;
m_buffer[m_pos] = sample;
m_pos = (m_pos+1)&(DC_ADJUST_BUFFERLEN-1);
}
static ymu8 *ym2149EnvInit(ymu8 *pEnv,ymint a,ymint b)
{
ymint i;
ymint d;
d = b-a;
a *= 15;
for (i=0;i<16;i++)
{
*pEnv++ = (ymu8)a;
a += d;
}
return pEnv;
}
CYm2149Ex::CYm2149Ex(ymu32 masterClock,ymint prediv,ymu32 playRate)
{
ymint i,env;
m_bFilter = true;
frameCycle = 0;
if (ymVolumeTable[15]==32767) // excuse me for that bad trick ;-)
{
for (i=0;i<16;i++)
{
ymVolumeTable[i] = (ymVolumeTable[i]*2)/6;
}
}
//--------------------------------------------------------
// build env shapes.
//--------------------------------------------------------
ymu8 *pEnv = &envData[0][0][0];
for (env=0;env<16;env++)
{
const ymint *pse = EnvWave[env];
for (ymint phase=0;phase<4;phase++)
{
pEnv = ym2149EnvInit(pEnv,pse[phase*2+0],pse[phase*2+1]);
}
}
internalClock = masterClock/prediv; // YM at 2Mhz on ATARI ST
replayFrequency = playRate; // DAC at 44.1Khz on PC
cycleSample = 0;
// Set volume voice pointers.
pVolA = &volA;
pVolB = &volB;
pVolC = &volC;
// Reset YM2149
reset();
}
CYm2149Ex::~CYm2149Ex()
{
}
void CYm2149Ex::setClock(ymu32 _clock)
{
internalClock = _clock;
}
ymu32 CYm2149Ex::toneStepCompute(ymu8 rHigh,ymu8 rLow)
{
ymint per = rHigh&15;
per = (per<<8)+rLow;
if (per<=5)
{
return 0;
}
#ifdef YM_INTEGER_ONLY
yms64 step = internalClock;
step <<= (15+16-3);
step /= (per * replayFrequency);
#else
ymfloat step = internalClock;
step /= ((ymfloat)per*8.0*(ymfloat)replayFrequency);
step *= 32768.0*65536.0;
#endif
ymu32 istep = (ymu32)step;
return istep;
}
ymu32 CYm2149Ex::noiseStepCompute(ymu8 rNoise)
{
ymint per = (rNoise&0x1f);
if (per<3)
return 0;
#ifdef YM_INTEGER_ONLY
yms64 step = internalClock;
step <<= (16-1-3);
step /= (per * replayFrequency);
#else
ymfloat step = internalClock;
step /= ((ymfloat)per*8.0*(ymfloat)replayFrequency);
step *= 65536.0/2.0;
#endif
return (ymu32)step;
}
ymu32 CYm2149Ex::rndCompute(void)
{
ymint rBit = (rndRack&1) ^ ((rndRack>>2)&1);
rndRack = (rndRack>>1) | (rBit<<16);
return (rBit ? 0 : 0xffff);
}
ymu32 CYm2149Ex::envStepCompute(ymu8 rHigh,ymu8 rLow)
{
ymint per = rHigh;
per = (per<<8)+rLow;
if (per<3)
return 0;
#ifdef YM_INTEGER_ONLY
yms64 step = internalClock;
step <<= (16+16-9);
step /= (per * replayFrequency);
#else
ymfloat step = internalClock;
step /= ((ymfloat)per*512.0*(ymfloat)replayFrequency);
step *= 65536.0*65536.0;
#endif
return (ymu32)step;
}
void CYm2149Ex::reset(void)
{
memset( registers, 0, sizeof(registers) );
for (int i=0;i<14;i++)
writeRegister(i,0);
writeRegister(7,0xff);
currentNoise = 0xffff;
rndRack = 1;
sidStop(0);
sidStop(1);
sidStop(2);
envShape = 0;
envPhase = 0;
envPos = 0;
m_dcAdjust.Reset();
memset(specialEffect,0,sizeof(specialEffect));
syncBuzzerStop();
m_lowPassFilter[0] = 0;
m_lowPassFilter[1] = 0;
}
void CYm2149Ex::sidVolumeCompute(ymint voice,ymint *pVol)
{
struct YmSpecialEffect *pVoice = specialEffect+voice;
if (pVoice->bSid)
{
if (pVoice->sidPos & (1<<31))
writeRegister(8+voice,pVoice->sidVol);
else
writeRegister(8+voice,0);
}
else if (pVoice->bDrum)
{
// writeRegister(8+voice,pVoice->drumData[pVoice->drumPos>>DRUM_PREC]>>4);
*pVol = (pVoice->drumData[pVoice->drumPos>>DRUM_PREC] * 255) / 6;
switch (voice)
{
case 0:
pVolA = &volA;
mixerTA = 0xffff;
mixerNA = 0xffff;
break;
case 1:
pVolB = &volB;
mixerTB = 0xffff;
mixerNB = 0xffff;
break;
case 2:
pVolC = &volC;
mixerTC = 0xffff;
mixerNC = 0xffff;
break;
}
pVoice->drumPos += pVoice->drumStep;
if ((pVoice->drumPos>>DRUM_PREC) >= pVoice->drumSize)
{
pVoice->bDrum = YMFALSE;
}
}
}
int CYm2149Ex::LowPassFilter(int in)
{
const int out = (m_lowPassFilter[0]>>2) + (m_lowPassFilter[1]>>1) + (in>>2);
m_lowPassFilter[0] = m_lowPassFilter[1];
m_lowPassFilter[1] = in;
return out;
}
ymsample CYm2149Ex::nextSample(void)
{
ymint vol;
ymint bt,bn;
if (noisePos&0xffff0000)
{
currentNoise ^= rndCompute();
noisePos &= 0xffff;
}
bn = currentNoise;
volE = ymVolumeTable[envData[envShape][envPhase][envPos>>(32-5)]];
sidVolumeCompute(0,&volA);
sidVolumeCompute(1,&volB);
sidVolumeCompute(2,&volC);
//---------------------------------------------------
// Tone+noise+env+DAC for three voices !
//---------------------------------------------------
bt = ((((yms32)posA)>>31) | mixerTA) & (bn | mixerNA);
vol = (*pVolA)&bt;
bt = ((((yms32)posB)>>31) | mixerTB) & (bn | mixerNB);
vol += (*pVolB)&bt;
bt = ((((yms32)posC)>>31) | mixerTC) & (bn | mixerNC);
vol += (*pVolC)&bt;
//---------------------------------------------------
// Inc
//---------------------------------------------------
posA += stepA;
posB += stepB;
posC += stepC;
noisePos += noiseStep;
envPos += envStep;
if (0 == envPhase)
{
if (envPos<envStep)
{
envPhase = 1;
}
}
syncBuzzerPhase += syncBuzzerStep;
if (syncBuzzerPhase&(1<<31))
{
envPos = 0;
envPhase = 0;
syncBuzzerPhase &= 0x7fffffff;
}
specialEffect[0].sidPos += specialEffect[0].sidStep;
specialEffect[1].sidPos += specialEffect[1].sidStep;
specialEffect[2].sidPos += specialEffect[2].sidStep;
//---------------------------------------------------
// Normalize process
//---------------------------------------------------
m_dcAdjust.AddSample(vol);
const int in = vol - m_dcAdjust.GetDcLevel();
return (m_bFilter) ? LowPassFilter(in) : in;
}
ymint CYm2149Ex::readRegister(ymint reg)
{
if ((reg>=0) && (reg<=13)) return registers[reg];
else return -1;
}
void CYm2149Ex::writeRegister(ymint reg,ymint data)
{
switch (reg)
{
case 0:
registers[0] = data&255;
stepA = toneStepCompute(registers[1],registers[0]);
if (!stepA) posA = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 2:
registers[2] = data&255;
stepB = toneStepCompute(registers[3],registers[2]);
if (!stepB) posB = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 4:
registers[4] = data&255;
stepC = toneStepCompute(registers[5],registers[4]);
if (!stepC) posC = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 1:
registers[1] = data&15;
stepA = toneStepCompute(registers[1],registers[0]);
if (!stepA) posA = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 3:
registers[3] = data&15;
stepB = toneStepCompute(registers[3],registers[2]);
if (!stepB) posB = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 5:
registers[5] = data&15;
stepC = toneStepCompute(registers[5],registers[4]);
if (!stepC) posC = (1<<31); // Assume output always 1 if 0 period (for Digi-sample !)
break;
case 6:
registers[6] = data&0x1f;
noiseStep = noiseStepCompute(registers[6]);
if (!noiseStep)
{
noisePos = 0;
currentNoise = 0xffff;
}
break;
case 7:
registers[7] = data&255;
mixerTA = (data&(1<<0)) ? 0xffff : 0;
mixerTB = (data&(1<<1)) ? 0xffff : 0;
mixerTC = (data&(1<<2)) ? 0xffff : 0;
mixerNA = (data&(1<<3)) ? 0xffff : 0;
mixerNB = (data&(1<<4)) ? 0xffff : 0;
mixerNC = (data&(1<<5)) ? 0xffff : 0;
break;
case 8:
registers[8] = data&31;
volA = ymVolumeTable[data&15];
if (data&0x10)
pVolA = &volE;
else
pVolA = &volA;
break;
case 9:
registers[9] = data&31;
volB = ymVolumeTable[data&15];
if (data&0x10)
pVolB = &volE;
else
pVolB = &volB;
break;
case 10:
registers[10] = data&31;
volC = ymVolumeTable[data&15];
if (data&0x10)
pVolC = &volE;
else
pVolC = &volC;
break;
case 11:
registers[11] = data&255;
envStep = envStepCompute(registers[12],registers[11]);
break;
case 12:
registers[12] = data&255;
envStep = envStepCompute(registers[12],registers[11]);
break;
case 13:
registers[13] = data&0xf;
envPos = 0;
envPhase = 0;
envShape = data&0xf;
break;
}
}
void CYm2149Ex::update(ymsample *pSampleBuffer,ymint nbSample)
{
ymsample *pBuffer = pSampleBuffer;
if (nbSample>0)
{
do
{
*pBuffer++ = nextSample();
}
while (--nbSample);
}
}
void CYm2149Ex::drumStart(ymint voice,ymu8 *pDrumBuffer,ymu32 drumSize,ymint drumFreq)
{
if ((pDrumBuffer) && (drumSize))
{
specialEffect[voice].drumData = pDrumBuffer;
specialEffect[voice].drumPos = 0;
specialEffect[voice].drumSize = drumSize;
specialEffect[voice].drumStep = (drumFreq<<DRUM_PREC)/replayFrequency;
specialEffect[voice].bDrum = YMTRUE;
}
}
void CYm2149Ex::drumStop(ymint voice)
{
specialEffect[voice].bDrum = YMFALSE;
}
void CYm2149Ex::sidStart(ymint voice,ymint timerFreq,ymint vol)
{
#ifdef YM_INTEGER_ONLY
ymu32 tmp = timerFreq * ((1<<31) / replayFrequency);
#else
ymfloat tmp = (ymfloat)timerFreq*((ymfloat)(1<<31))/(ymfloat)replayFrequency;
#endif
specialEffect[voice].sidStep = (ymu32)tmp;
specialEffect[voice].sidVol = vol&15;
specialEffect[voice].bSid = YMTRUE;
}
void CYm2149Ex::sidSinStart(ymint voice,ymint timerFreq,ymint vol)
{
// TODO
}
void CYm2149Ex::sidStop(ymint voice)
{
specialEffect[voice].bSid = YMFALSE;
}
void CYm2149Ex::syncBuzzerStart(ymint timerFreq,ymint _envShape)
{
#ifdef YM_INTEGER_ONLY
ymu32 tmp = timerFreq * ((1<<31) / replayFrequency);
#else
ymfloat tmp = (ymfloat)timerFreq*((ymfloat)(1<<31))/(ymfloat)replayFrequency;
#endif
envShape = _envShape&15;
syncBuzzerStep = (ymu32)tmp;
syncBuzzerPhase = 0;
bSyncBuzzer = YMTRUE;
}
void CYm2149Ex::syncBuzzerStop(void)
{
bSyncBuzzer = YMFALSE;
syncBuzzerPhase = 0;
syncBuzzerStep = 0;
}