Skip to content

Commit

Permalink
Zelda HLE: Implement the PCM8 sample source.
Browse files Browse the repository at this point in the history
Reorder some things in the source code to match the sample source definition
order. Add and remove some TODOs.
  • Loading branch information
delroth committed Dec 20, 2014
1 parent 162c2bd commit 672aa72
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 38 deletions.
126 changes: 92 additions & 34 deletions Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp
Expand Up @@ -357,7 +357,9 @@ struct ZeldaAudioRenderer::VPB
// If non zero, reset some value in the VPB when processing it.
u16 reset_vpb;

u16 unk_05;
// If non zero, tells PCM8/PCM16 sample sources that the end of the voice
// has been reached and looping should be considered if enabled.
u16 end_reached;

// If non zero, input samples to this VPB will be the fixed value from
// VPB[33] (constant_sample_value). This is used when a voice is being
Expand Down Expand Up @@ -482,6 +484,9 @@ struct ZeldaAudioRenderer::VPB
// Simple saw wave at 100% amplitude and frequency controlled via the
// resampling ratio.
SRC_SAW_WAVE = 1,
// Samples stored in ARAM in PCM8 format, at an arbitrary sampling rate
// (resampling is applied).
SRC_PCM8_FROM_ARAM = 8,
// Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded,
// at an arbitrary sample rate (resampling is applied).
SRC_AFC_HQ_FROM_ARAM = 9,
Expand Down Expand Up @@ -549,9 +554,6 @@ void ZeldaAudioRenderer::PrepareFrame()
if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0)
PanicAlert("Zelda HLE using back mixing buffers");

m_buf_back_left.fill(0);
m_buf_back_right.fill(0);

// TODO: Dolby/reverb mixing here.

m_prepared = true;
Expand Down Expand Up @@ -791,6 +793,12 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb)
break;
}

case VPB::SRC_PCM8_FROM_ARAM:
DownloadPCM8SamplesFromARAM(raw_input_samples.data() + 4, vpb,
NeededRawSamplesCount(*vpb));
Resample(vpb, raw_input_samples.data(), buffer);
break;

case VPB::SRC_AFC_HQ_FROM_ARAM:
DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb,
NeededRawSamplesCount(*vpb));
Expand Down Expand Up @@ -861,44 +869,52 @@ void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst)
vpb->current_pos_frac = pos & 0xFFF;
}

void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM(
void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM(
s16* dst, VPB* vpb, u16 requested_samples_count)
{
u32 addr = vpb->GetBaseAddress() + vpb->current_position_h * sizeof (u16);
s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr);

if (requested_samples_count > vpb->GetRemainingLength())
if (vpb->done)
{
s16 last_sample = 0;
for (u16 i = 0; i < vpb->GetRemainingLength(); ++i)
*dst++ = last_sample = Common::swap16(*src_ptr++);
for (u16 i = vpb->GetRemainingLength(); i < requested_samples_count; ++i)
*dst++ = last_sample;
for (u16 i = 0; i < requested_samples_count; ++i)
dst[i] = 0;
return;
}

vpb->current_position_h += vpb->GetRemainingLength();
vpb->SetRemainingLength(0);
vpb->done = true;
if (!vpb->reset_vpb)
{
vpb->end_reached = false;
}
else
while (requested_samples_count)
{
vpb->SetRemainingLength(vpb->GetRemainingLength() - requested_samples_count);
vpb->samples_before_loop = vpb->loop_start_position_h - vpb->current_position_h;
if (requested_samples_count <= vpb->samples_before_loop)
if (vpb->end_reached)
{
for (u16 i = 0; i < requested_samples_count; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->current_position_h += requested_samples_count;
}
else
{
for (u16 i = 0; i < vpb->samples_before_loop; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->SetBaseAddress(vpb->GetLoopAddress());
src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetLoopAddress());
for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->current_position_h = requested_samples_count - vpb->samples_before_loop;
vpb->end_reached = false;
if (!vpb->is_looping)
{
for (u16 i = 0; i < requested_samples_count; ++i)
dst[i] = 0;
vpb->done = true;
break;
}
vpb->SetCurrentPosition(vpb->GetLoopAddress());
}

vpb->SetRemainingLength(
vpb->GetLoopStartPosition() - vpb->GetCurrentPosition());
vpb->SetCurrentARAMAddr(
vpb->GetBaseAddress() + vpb->GetCurrentPosition());

s8* src_ptr = (s8*)DSP::GetARAMPtr() + vpb->GetCurrentARAMAddr();
u16 samples_to_download = std::min(vpb->GetRemainingLength(),
(u32)requested_samples_count);

for (u16 i = 0; i < samples_to_download; ++i)
*dst++ = *src_ptr++ << 8;

vpb->SetRemainingLength(vpb->GetRemainingLength() - samples_to_download);
vpb->SetCurrentARAMAddr(vpb->GetCurrentARAMAddr() + samples_to_download);
requested_samples_count -= samples_to_download;
if (!vpb->GetRemainingLength())
vpb->end_reached = true;
}
}

Expand Down Expand Up @@ -1065,8 +1081,50 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count)
}
}

void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM(
s16* dst, VPB* vpb, u16 requested_samples_count)
{
u32 addr = vpb->GetBaseAddress() + vpb->current_position_h * sizeof (u16);
s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr);

if (requested_samples_count > vpb->GetRemainingLength())
{
s16 last_sample = 0;
for (u16 i = 0; i < vpb->GetRemainingLength(); ++i)
*dst++ = last_sample = Common::swap16(*src_ptr++);
for (u16 i = vpb->GetRemainingLength(); i < requested_samples_count; ++i)
*dst++ = last_sample;

vpb->current_position_h += vpb->GetRemainingLength();
vpb->SetRemainingLength(0);
vpb->done = true;
}
else
{
vpb->SetRemainingLength(vpb->GetRemainingLength() - requested_samples_count);
vpb->samples_before_loop = vpb->loop_start_position_h - vpb->current_position_h;
if (requested_samples_count <= vpb->samples_before_loop)
{
for (u16 i = 0; i < requested_samples_count; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->current_position_h += requested_samples_count;
}
else
{
for (u16 i = 0; i < vpb->samples_before_loop; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->SetBaseAddress(vpb->GetLoopAddress());
src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetLoopAddress());
for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i)
*dst++ = Common::swap16(*src_ptr++);
vpb->current_position_h = requested_samples_count - vpb->samples_before_loop;
}
}
}

void ZeldaAudioRenderer::DoState(PointerWrap& p)
{
// TODO(delroth): Add all the state here.
p.Do(m_output_lbuf_addr);
p.Do(m_output_rbuf_addr);
}
12 changes: 8 additions & 4 deletions Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h
Expand Up @@ -121,15 +121,19 @@ class ZeldaAudioRenderer
// Coefficients used for resampling.
std::array<s16, 0x100> m_resampling_coeffs{};

// Downloads samples from MRAM while handling appropriate length / looping
// behavior.
void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count);
// Downloads PCM8 encoded samples from ARAM. Handles looping and other
// parameters appropriately.
void DownloadPCM8SamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count);

// Download AFC encoded samples from ARAM and decode them. Handles looping
// Downloads AFC encoded samples from ARAM and decode them. Handles looping
// and other parameters appropriately.
void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count);
void DecodeAFC(VPB* vpb, s16* dst, size_t block_count);
std::array<s16, 0x20> m_afc_coeffs{};

// Downloads samples from MRAM while handling appropriate length / looping
// behavior.
void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count);
};

class ZeldaUCode : public UCodeInterface
Expand Down

0 comments on commit 672aa72

Please sign in to comment.