Skip to content

Commit

Permalink
v0.4: per voice visualization oscilloscopes, 2MiB emulated Atari mach…
Browse files Browse the repository at this point in the history
…ine, AtariAudio API cleanup, some optimizations
  • Loading branch information
arnaud-carre committed Aug 18, 2023
1 parent cbd4e54 commit 3d9cfcb
Show file tree
Hide file tree
Showing 17 changed files with 405 additions and 120 deletions.
45 changes: 13 additions & 32 deletions AtariAudio/AtariMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@ void AtariMachine::Startup(uint32_t hostReplayRate)
gCurrentMachine = NULL;
}

bool AtariMachine::Upload(const void* src, int addr, int size)
bool AtariMachine::Upload(const void* src, uint32_t addr, uint32_t size)
{
if (uint32_t(addr + size) > RAM_SIZE)
if (addr + size > RAM_SIZE)
return false;

if ((NULL == src) || (0 == size))
Expand All @@ -287,8 +287,7 @@ void AtariMachine::ConfigureReturnByRte()

bool AtariMachine::JmpBinary(int pc, int timeOut50Hz)
{
m68k_write_memory_32(0x14, 0x500); // DIV by ZERO excep jump at $500
memWrite16(0x500, 0x4e73); // $500 is just RTE
m68k_write_memory_32(0x14, RTE_INSTRUCTION_ADDR); // DIV by ZERO excep jump at $500

m68k_write_memory_32(4, pc); // pc at next RESET
m68k_pulse_reset(); // reset CPU & start execution at PC
Expand All @@ -304,31 +303,27 @@ bool AtariMachine::JmpBinary(int pc, int timeOut50Hz)
return (kReset == m_ExitCode);
}

bool AtariMachine::SndhInit(const void* data, int dataSize, int d0)
bool AtariMachine::Jsr(uint32_t addr, uint32_t d0)
{
gCurrentMachine = this;

bool ret = false;
// upload data in RAM
if (Upload(data, SNDH_UPLOAD_ADDR, dataSize))
{
ConfigureReturnByRts();
m68k_set_reg(M68K_REG_D0, d0);

if (JmpBinary(SNDH_UPLOAD_ADDR, 50*10)) // timeout of 1sec for init
{
ret = true;
}
}
ConfigureReturnByRts();
m68k_set_reg(M68K_REG_D0, d0);
ret = JmpBinary(addr, 50*10); // timeout of 1sec for init
gCurrentMachine = NULL;
return ret;
}

int16_t AtariMachine::ComputeNextSample()
int16_t AtariMachine::ComputeNextSample(uint32_t* pSampleDebugInfo)
{
gCurrentMachine = this;
int32_t level = m_Ym2149.ComputeNextSample();
level += m_SteDac.ComputeNextSample((const int8_t*)m_RAM, RAM_SIZE, m_Mfp);
int32_t level = m_Ym2149.ComputeNextSample(pSampleDebugInfo);
int32_t steLevel = m_SteDac.ComputeNextSample((const int8_t*)m_RAM, RAM_SIZE, m_Mfp);
if (steLevel && (pSampleDebugInfo))
*pSampleDebugInfo |= (steLevel >> 8) << 24;
level += steLevel;

if (level > 32767)
level = 32767;
Expand All @@ -353,17 +348,3 @@ int16_t AtariMachine::ComputeNextSample()
gCurrentMachine = NULL;
return out;
}

bool AtariMachine::SndhPlayerTick()
{
// printf("--------- frame tick -------------\n");
gCurrentMachine = this;
bool ret = false;
ConfigureReturnByRts();
if (JmpBinary(SNDH_UPLOAD_ADDR+8, 1)) // +8 is player tick in SNDH (timeout of 1VBL)
{
ret = true;
}
gCurrentMachine = NULL;
return ret;
}
12 changes: 6 additions & 6 deletions AtariAudio/AtariMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
#include "Mk68901.h"
#include "SteDac.h"

static const uint32_t RAM_SIZE = 1024 * 1024;
static const uint32_t RAM_SIZE = 2*1024*1024;
static const uint32_t RTE_INSTRUCTION_ADDR = 0x500;
static const uint32_t RESET_INSTRUCTION_ADDR = 0x502;
static const uint32_t SNDH_UPLOAD_ADDR = 0x10000; // some SNDH can't play below (ie SynthDream2)
static const uint32_t GEMDOS_MALLOC_EMUL_BUFFER = RAM_SIZE-0x80000;
static const uint32_t GEMDOS_MALLOC_EMUL_BUFFER = RAM_SIZE-0x100000;


class AtariMachine
{
Expand All @@ -29,9 +30,9 @@ class AtariMachine
};

void Startup(uint32_t hostReplayRate);
bool SndhInit(const void* data, int dataSize, int d0);
bool SndhPlayerTick();
int16_t ComputeNextSample();
bool Upload(const void* src, uint32_t addr, uint32_t size);
bool Jsr(uint32_t addr, uint32_t d0);
int16_t ComputeNextSample(uint32_t* pSampleDebugInfo = NULL);

unsigned int memRead8(unsigned int address);
unsigned int memRead16(unsigned int address);
Expand All @@ -45,7 +46,6 @@ class AtariMachine
void ConfigureReturnByRts();
void ConfigureReturnByRte();
bool JmpBinary(int pc, int timeOut50Hz);
bool Upload(const void* src, int addr, int size);

uint8_t* m_RAM;
int m_ExitCode;
Expand Down
14 changes: 9 additions & 5 deletions AtariAudio/SndhFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,20 +220,22 @@ bool SndhFile::InitSubSong(int subSongId)
m_frameCount = info.playerTickCount;
m_loopCount = 0;
m_atariMachine.Startup(m_hostReplayRate);
if (m_atariMachine.SndhInit(m_rawBuffer, m_rawSize, subSongId))
ret = true;
if (m_atariMachine.Upload(m_rawBuffer, SNDH_UPLOAD_ADDR, m_rawSize))
{
ret = m_atariMachine.Jsr(SNDH_UPLOAD_ADDR, subSongId);
}
return ret;
}

int SndhFile::AudioRender(int16_t* buffer, int count)
int SndhFile::AudioRender(int16_t* buffer, int count, uint32_t* pSampleViewInfo)
{
for (int i = 0; i < count; i++)
{
m_innerSamplePos--;
// check if we should call SNDH music driver tick (most of the time 50hz)
if (m_innerSamplePos <= 0)
{
m_atariMachine.SndhPlayerTick();
m_atariMachine.Jsr(SNDH_UPLOAD_ADDR + 8, 0);
m_innerSamplePos = m_samplePerTick;
m_frame++;
if (m_frame >= m_frameCount)
Expand All @@ -243,7 +245,9 @@ int SndhFile::AudioRender(int16_t* buffer, int count)
}

// compute the Atari machine sample (YM2149 and STE DAC)
*buffer++ = m_atariMachine.ComputeNextSample();
*buffer++ = m_atariMachine.ComputeNextSample(pSampleViewInfo);
if (pSampleViewInfo)
pSampleViewInfo++;
}
return m_loopCount;
}
12 changes: 11 additions & 1 deletion AtariAudio/SndhFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ class SndhFile
int GetDefaultSubsong() const { return m_defaultSubSong; }
bool GetSubsongInfo(int subSongId, SubSongInfo& out) const;
bool InitSubSong(int subSongId);
int AudioRender(int16_t* buffer, int count);

/*
* Main audio rendering function.
* Compute the next "count" samples into "buffer" (mono, signed, 16bits samples)
* pSampleViewInfo is an optional buffer of "count" uint32_t for gadget visualization purpose
* The 32bits contains four 8bits signed values that are respectivly from low byte to high byte:
* YM voices A,B,C and STE DAC
* Note: Always use "buffer" as audio source. Do *not* mix yourself the SampleViewInfo data
* It's visualization data only!
*/
int AudioRender(int16_t* buffer, int count, uint32_t* pSampleViewInfo = NULL);

const void* GetRawData() const { return m_rawBuffer; }
const int GetRawDataSize() const { return m_rawSize; }
Expand Down
28 changes: 19 additions & 9 deletions AtariAudio/ym2149_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ static const uint8_t s_envData[10 * 64] =
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
};

#if D_USE_MEASURED_MIXING_TABLE
// All possible volume mixing combine, in ST-Replay mode (ie when all voices ON)
// Data measured on real hardware by Paulo Simoes in 2012
static const uint16_t sSTReplayTable[816] =
Expand Down Expand Up @@ -98,6 +97,15 @@ static const uint16_t sSTReplayTable[816] =
0x03e7,0x035f,0x02b4,0x02d3,0x022c,0x0181,0x035f,0x02d7,0x0229,0x024d,0x01a3,0x00fb,0x01c5,0x011b,0x0078,0x0000
};

#define k15toS8(a) ((((a*127)>>15)+63)^0x80) // signed 8bits value for oscillators viewing display per voice
static const uint32_t s_ViewVolTab[16] =
{
k15toS8(62),k15toS8(161),k15toS8(265),k15toS8(377),k15toS8(580),k15toS8(774),k15toS8(1155),k15toS8(1575),
k15toS8(2260),k15toS8(3088),k15toS8(4570),k15toS8(6233),k15toS8(9330),k15toS8(13187),k15toS8(21220),k15toS8(32767)
};

static uint32_t s444to888[0x1000];

static uint16_t s_ymMixingVolumeTable[16 * 16 * 16];
static void InitYmTable(uint16_t* pOut, const uint16_t* pIn)
{
Expand All @@ -117,12 +125,14 @@ static void InitYmTable(uint16_t* pOut, const uint16_t* pIn)
}
}
}

// init debug view table
for (int i = 0; i < 0x1000; i++)
{
uint32_t v;
v = s_ViewVolTab[(i >> 0) & 15] << 0;
v |= s_ViewVolTab[(i >> 4) & 15] << 8;
v |= s_ViewVolTab[(i >> 8) & 15] << 16;
s444to888[i] = v;
}
}
#else
#define k15toD3(a) ((a)/3) // 15bits input, and 3 YM voices
static const uint16_t s_volTab[16] =
{
k15toD3(62),k15toD3(161),k15toD3(265),k15toD3(377),k15toD3(580),k15toD3(774),k15toD3(1155),k15toD3(1575),
k15toD3(2260),k15toD3(3088),k15toD3(4570),k15toD3(6233),k15toD3(9330),k15toD3(13187),k15toD3(21220),k15toD3(32767)
};
#endif
68 changes: 37 additions & 31 deletions AtariAudio/ym2149c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
--------------------------------------------------------------------*/
// Tiny & cycle accurate ym2149 emulation.
// operate at original YM freq divided by 8 (so 250Khz, as nothing runs faster in the chip)
#include <assert.h>
#include "ym2149c.h"
#include "ym2149_tables.h"

Expand All @@ -14,15 +15,13 @@

void Ym2149c::Reset(uint32_t hostReplayRate, uint32_t ymClock)
{
#if D_USE_MEASURED_MIXING_TABLE
InitYmTable(s_ymMixingVolumeTable, sSTReplayTable);
#endif
for (int v = 0; v < 3; v++)
{
m_toneCounter[v] = 0;
m_tonePeriod[v] = 0;
m_toneEdge[v] = 0;
}
m_toneEdges = 0;
m_insideTimerIrq = false;
m_hostReplayRate = hostReplayRate;
m_ymClockOneEighth = ymClock/8;
Expand All @@ -35,6 +34,7 @@ void Ym2149c::Reset(uint32_t hostReplayRate, uint32_t ymClock)
m_currentLevel = 0;
m_innerCycle = 0;
m_envPos = 0;
m_currentDebugThreeVoices = 0;
}

void Ym2149c::WritePort(uint8_t port, uint8_t value)
Expand Down Expand Up @@ -74,8 +74,11 @@ void Ym2149c::WriteReg(int reg, uint8_t value)
m_noisePeriod = m_regs[6];
break;
case 7:
m_toneMask = value & 0x7;
m_noiseMask = (value>>3) & 0x7;
{
static const uint32_t masks[8] = { 0x000,0x00f,0x0f0, 0x0ff, 0xf00, 0xf0f, 0xff0, 0xfff };
m_toneMask = masks[value & 0x7];
m_noiseMask = masks[(value >> 3) & 0x7];
}
break;
case 11:
case 12:
Expand Down Expand Up @@ -117,33 +120,26 @@ int16_t Ym2149c::dcAdjust(uint16_t v)
// Tick internal YM2149 state machine at 250Khz ( 2Mhz/8 )
uint16_t Ym2149c::Tick()
{
uint16_t output = 0;
for (int v = 0; v < 3; v++)
{
const uint32_t vmask = ((m_toneEdge[v]) | (m_toneMask >> v)) &
(m_currentNoiseBit | (m_noiseMask >> v));
if (vmask & 1)
{
int level = m_regs[8 + v];
if (level & 0x10)
level = m_pCurrentEnv[m_envPos+32];
#if D_USE_MEASURED_MIXING_TABLE
output = (output<<4)|level;
#else
output += s_volTab[level];
#endif
}
}
#if D_USE_MEASURED_MIXING_TABLE
output = s_ymMixingVolumeTable[output];
#endif

const uint32_t envLevel = m_pCurrentEnv[m_envPos + 32];
uint32_t levels;
levels = ((m_regs[8] & 0x10) ? envLevel : m_regs[8]) << 0;
levels |= ((m_regs[9] & 0x10) ? envLevel : m_regs[9]) << 4;
levels |= ((m_regs[10] & 0x10) ? envLevel : m_regs[10]) << 8;

// three voices at same time
const uint32_t vmask = (m_toneEdges | m_toneMask) & (m_currentNoiseBit | m_noiseMask);
levels &= vmask;
m_currentDebugThreeVoices = levels;
uint32_t output = s_ymMixingVolumeTable[levels];

// update internal state
for (int v = 0; v < 3; v++)
{
m_toneCounter[v]++;
if (m_toneCounter[v] >= m_tonePeriod[v])
{
m_toneEdge[v] ^= 1;
m_toneEdges ^= 0xf<<(v*4);
m_toneCounter[v] = 0;
}
}
Expand All @@ -156,8 +152,8 @@ uint16_t Ym2149c::Tick()
m_noiseCounter++;
if (m_noiseCounter >= m_noisePeriod)
{
m_currentNoiseBit = (m_noiseRndRack & 1) ^ ((m_noiseRndRack >> 2) & 1);
m_noiseRndRack = (m_noiseRndRack >> 1) | (m_currentNoiseBit << 16);
m_currentNoiseBit = ((m_noiseRndRack ^ (m_noiseRndRack >> 2))&1) ? ~0 : 0;
m_noiseRndRack = (m_noiseRndRack >> 1) | ((m_currentNoiseBit&1) << 16);
m_noiseCounter = 0;
}

Expand All @@ -175,19 +171,29 @@ uint16_t Ym2149c::Tick()

// called at host replay rate ( like 48Khz )
// internally update YM chip state machine at 250Khz and average output for each host sample
int16_t Ym2149c::ComputeNextSample()
int16_t Ym2149c::ComputeNextSample(uint32_t* pSampleDebugInfo)
{
#if D_DOWNSAMPLE_USE_MAX_VALUE
uint16_t tickLevel = 0;
uint32_t debugValues = 0x000;
do
{
uint16_t level = Tick();
tickLevel = (level > tickLevel) ? level : tickLevel;
if (level > tickLevel)
{
tickLevel = level;
debugValues = m_currentDebugThreeVoices;
}
m_innerCycle += m_hostReplayRate;
}
while (m_innerCycle < m_ymClockOneEighth);
m_innerCycle -= m_ymClockOneEighth;
int16_t out = dcAdjust(tickLevel);
if (pSampleDebugInfo)
{
assert(debugValues < 0x1000);
*pSampleDebugInfo = s444to888[debugValues];
}
#else
// down-sample by averaging samples
uint16_t tickLevel;
Expand Down Expand Up @@ -221,7 +227,7 @@ void Ym2149c::InsideTimerIrq(bool inside)
{
if (m_edgeNeedReset[v])
{
m_toneEdge[v] ^= 1;
m_toneEdges ^= 0xf<<(v*4);
m_toneCounter[v] = 0;
m_edgeNeedReset[v] = false;
}
Expand Down
Loading

0 comments on commit 3d9cfcb

Please sign in to comment.