2021-03-02 02:13:17 +00:00
|
|
|
// Project64 - A Nintendo 64 emulator
|
2021-05-18 11:51:36 +00:00
|
|
|
// https://www.pj64-emu.com/
|
2021-03-02 02:13:17 +00:00
|
|
|
// Copyright(C) 2001-2021 Project64
|
|
|
|
// Copyright(C) 2000-2015 Azimer
|
|
|
|
// GNU/GPLv2 licensed: https://gnu.org/licenses/gpl-2.0.html
|
2021-03-27 09:10:14 +00:00
|
|
|
|
2017-09-12 07:58:35 +00:00
|
|
|
#include "SoundBase.h"
|
2017-09-21 13:20:36 +00:00
|
|
|
#include <Common/Util.h>
|
|
|
|
#include <Project64-audio/AudioSettings.h>
|
|
|
|
#include <Project64-audio/AudioMain.h>
|
|
|
|
#include <Project64-audio/trace.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
SoundDriverBase::SoundDriverBase() :
|
|
|
|
m_MaxBufferSize(MAX_SIZE),
|
2021-04-12 11:35:39 +00:00
|
|
|
m_AI_DMAPrimaryBuffer(nullptr),
|
|
|
|
m_AI_DMASecondaryBuffer(nullptr),
|
2017-09-21 13:20:36 +00:00
|
|
|
m_AI_DMAPrimaryBytes(0),
|
|
|
|
m_AI_DMASecondaryBytes(0),
|
|
|
|
m_CurrentReadLoc(0),
|
|
|
|
m_CurrentWriteLoc(0),
|
2017-09-26 13:57:33 +00:00
|
|
|
m_BufferRemaining(0)
|
2017-09-21 13:20:36 +00:00
|
|
|
{
|
|
|
|
memset(&m_Buffer, 0, sizeof(m_Buffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoundDriverBase::Initialize()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-13 00:37:49 +00:00
|
|
|
void SoundDriverBase::AI_SetFrequency(uint32_t Frequency, uint32_t BufferSize)
|
2017-09-21 13:20:36 +00:00
|
|
|
{
|
2017-10-13 00:37:49 +00:00
|
|
|
SetFrequency(Frequency, BufferSize);
|
2019-01-21 05:45:52 +00:00
|
|
|
m_MaxBufferSize = (BufferSize * 8);
|
2022-04-10 23:35:04 +00:00
|
|
|
if (m_MaxBufferSize > MAX_SIZE)
|
|
|
|
{
|
|
|
|
m_MaxBufferSize = MAX_SIZE;
|
|
|
|
}
|
2017-09-21 13:20:36 +00:00
|
|
|
m_CurrentReadLoc = m_CurrentWriteLoc = m_BufferRemaining = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::AI_LenChanged(uint8_t *start, uint32_t length)
|
|
|
|
{
|
|
|
|
WriteTrace(TraceAudioDriver, TraceDebug, "Start");
|
|
|
|
|
|
|
|
// Bleed off some of this buffer to smooth out audio
|
2019-01-16 22:53:49 +00:00
|
|
|
if (g_settings->SyncAudio() || !g_settings->FullSpeed())
|
2017-09-21 13:20:36 +00:00
|
|
|
{
|
|
|
|
while ((m_BufferRemaining) == m_MaxBufferSize)
|
|
|
|
{
|
|
|
|
pjutil::Sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CGuard guard(m_CS);
|
|
|
|
BufferAudio();
|
|
|
|
|
2021-04-12 11:35:39 +00:00
|
|
|
if (m_AI_DMASecondaryBuffer != nullptr)
|
2017-09-21 13:20:36 +00:00
|
|
|
{
|
|
|
|
WriteTrace(TraceAudioDriver, TraceDebug, "Discarding previous secondary buffer");
|
|
|
|
}
|
|
|
|
m_AI_DMASecondaryBuffer = start;
|
|
|
|
m_AI_DMASecondaryBytes = length;
|
|
|
|
if (m_AI_DMAPrimaryBytes == 0)
|
|
|
|
{
|
|
|
|
m_AI_DMAPrimaryBuffer = m_AI_DMASecondaryBuffer;
|
2021-04-12 11:35:39 +00:00
|
|
|
m_AI_DMASecondaryBuffer = nullptr;
|
2017-09-21 13:20:36 +00:00
|
|
|
m_AI_DMAPrimaryBytes = m_AI_DMASecondaryBytes;
|
|
|
|
m_AI_DMASecondaryBytes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*g_AudioInfo.AI_STATUS_REG = AI_STATUS_DMA_BUSY;
|
|
|
|
if (m_AI_DMAPrimaryBytes > 0 && m_AI_DMASecondaryBytes > 0)
|
|
|
|
{
|
|
|
|
*g_AudioInfo.AI_STATUS_REG = (uint32_t)(AI_STATUS_DMA_BUSY | AI_STATUS_FIFO_FULL);
|
|
|
|
}
|
|
|
|
BufferAudio();
|
|
|
|
WriteTrace(TraceAudioDriver, TraceDebug, "Done");
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::AI_Startup()
|
|
|
|
{
|
|
|
|
WriteTrace(TraceAudioDriver, TraceDebug, "Start");
|
|
|
|
m_AI_DMAPrimaryBytes = m_AI_DMASecondaryBytes = 0;
|
2021-04-12 11:35:39 +00:00
|
|
|
m_AI_DMAPrimaryBuffer = m_AI_DMASecondaryBuffer = nullptr;
|
2017-09-21 13:20:36 +00:00
|
|
|
m_MaxBufferSize = MAX_SIZE;
|
|
|
|
m_CurrentReadLoc = m_CurrentWriteLoc = m_BufferRemaining = 0;
|
|
|
|
if (Initialize())
|
|
|
|
{
|
|
|
|
StartAudio();
|
|
|
|
}
|
|
|
|
WriteTrace(TraceAudioDriver, TraceDebug, "Start");
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::AI_Shutdown()
|
|
|
|
{
|
|
|
|
StopAudio();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::AI_Update(bool Wait)
|
|
|
|
{
|
|
|
|
m_AiUpdateEvent.IsTriggered(Wait ? SyncEvent::INFINITE_TIMEOUT : 0);
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:45:18 +00:00
|
|
|
uint32_t SoundDriverBase::AI_ReadLength()
|
|
|
|
{
|
|
|
|
CGuard guard(m_CS);
|
2017-09-28 19:32:43 +00:00
|
|
|
return m_AI_DMAPrimaryBytes & ~0x7;
|
2017-09-22 21:45:18 +00:00
|
|
|
}
|
|
|
|
|
2017-09-21 13:20:36 +00:00
|
|
|
void SoundDriverBase::LoadAiBuffer(uint8_t *start, uint32_t length)
|
|
|
|
{
|
2019-08-01 04:15:26 +00:00
|
|
|
static uint8_t nullBuff[MAX_SIZE];
|
2021-04-12 11:35:39 +00:00
|
|
|
uint8_t *ptrStart = start != nullptr ? start : nullBuff;
|
2017-09-21 13:20:36 +00:00
|
|
|
uint32_t writePtr = 0, bytesToMove = length;
|
|
|
|
|
|
|
|
if (bytesToMove > m_MaxBufferSize)
|
|
|
|
{
|
|
|
|
memset(ptrStart, 0, 100);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool DMAEnabled = (*g_AudioInfo.AI_CONTROL_REG & AI_CONTROL_DMA_ON) == AI_CONTROL_DMA_ON;
|
|
|
|
if (!DMAEnabled)
|
|
|
|
{
|
2021-05-18 11:51:36 +00:00
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Return silence - DMA is disabled");
|
2017-09-21 13:20:36 +00:00
|
|
|
memset(ptrStart, 0, bytesToMove);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGuard guard(m_CS);
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Step 0: Replace depleted stored buffer for next run");
|
|
|
|
BufferAudio();
|
|
|
|
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Step 1: Deplete stored buffer (bytesToMove: 0x%08X m_BufferRemaining: 0x%08X)", bytesToMove, m_BufferRemaining);
|
|
|
|
while (bytesToMove > 0 && m_BufferRemaining > 0)
|
|
|
|
{
|
|
|
|
*(uint32_t *)(ptrStart + writePtr) = *(uint32_t *)(m_Buffer + m_CurrentReadLoc);
|
|
|
|
m_CurrentReadLoc += 4;
|
|
|
|
writePtr += 4;
|
|
|
|
m_CurrentReadLoc %= m_MaxBufferSize;
|
|
|
|
m_BufferRemaining -= 4;
|
|
|
|
bytesToMove -= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Step 2: Fill bytesToMove (0x%08X) with silence", bytesToMove);
|
|
|
|
while (bytesToMove > 0)
|
|
|
|
{
|
|
|
|
*(uint8_t *)(ptrStart + writePtr) = 0;
|
|
|
|
writePtr += 1;
|
|
|
|
bytesToMove -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Step 3: Replace depleted stored buffer for next run");
|
|
|
|
BufferAudio();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::BufferAudio()
|
|
|
|
{
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Start (m_BufferRemaining: 0x%08X m_MaxBufferSize: 0x%08X m_AI_DMAPrimaryBytes: 0x%08X m_AI_DMASecondaryBytes: 0x%08X)", m_BufferRemaining, m_MaxBufferSize, m_AI_DMAPrimaryBytes, m_AI_DMASecondaryBytes);
|
|
|
|
while ((m_BufferRemaining < m_MaxBufferSize) && (m_AI_DMAPrimaryBytes > 0 || m_AI_DMASecondaryBytes > 0))
|
|
|
|
{
|
|
|
|
*(uint16_t *)(m_Buffer + m_CurrentWriteLoc) = *(uint16_t *)(m_AI_DMAPrimaryBuffer + 2);
|
|
|
|
*(uint16_t *)(m_Buffer + m_CurrentWriteLoc + 2) = *(uint16_t *)m_AI_DMAPrimaryBuffer;
|
|
|
|
m_CurrentWriteLoc += 4;
|
|
|
|
m_AI_DMAPrimaryBuffer += 4;
|
|
|
|
m_CurrentWriteLoc %= m_MaxBufferSize;
|
|
|
|
m_BufferRemaining += 4;
|
|
|
|
m_AI_DMAPrimaryBytes -= 4;
|
|
|
|
if (m_AI_DMAPrimaryBytes == 0)
|
|
|
|
{
|
2021-03-27 09:10:14 +00:00
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Emptied primary buffer");
|
2017-09-21 13:20:36 +00:00
|
|
|
m_AI_DMAPrimaryBytes = m_AI_DMASecondaryBytes; m_AI_DMAPrimaryBuffer = m_AI_DMASecondaryBuffer; // Switch
|
2021-04-12 11:35:39 +00:00
|
|
|
m_AI_DMASecondaryBytes = 0; m_AI_DMASecondaryBuffer = nullptr;
|
2017-09-21 13:20:36 +00:00
|
|
|
*g_AudioInfo.AI_STATUS_REG = AI_STATUS_DMA_BUSY;
|
|
|
|
*g_AudioInfo.AI_STATUS_REG &= ~AI_STATUS_FIFO_FULL;
|
|
|
|
*g_AudioInfo.MI_INTR_REG |= MI_INTR_AI;
|
|
|
|
g_AudioInfo.CheckInterrupts();
|
|
|
|
if (m_AI_DMAPrimaryBytes == 0)
|
|
|
|
{
|
|
|
|
*g_AudioInfo.AI_STATUS_REG = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WriteTrace(TraceAudioDriver, TraceVerbose, "Done (m_BufferRemaining: 0x%08X)", m_BufferRemaining);
|
|
|
|
}
|
|
|
|
|
2017-10-13 00:37:49 +00:00
|
|
|
void SoundDriverBase::SetFrequency(uint32_t /*Frequency*/, uint32_t /*Divider*/)
|
2017-09-21 13:20:36 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::StartAudio()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriverBase::StopAudio()
|
|
|
|
{
|
2017-10-13 00:37:49 +00:00
|
|
|
}
|