mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 15:04:16 +01:00
Implement an ADPCM decoder for audren
This is used by many retail games, it only supports mono sound. This implementation is based on Ryu's.
This commit is contained in:
parent
b1e15efbab
commit
30936ce6dc
@ -37,6 +37,7 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/audio.cpp
|
||||
${source_DIR}/skyline/audio/track.cpp
|
||||
${source_DIR}/skyline/audio/resampler.cpp
|
||||
${source_DIR}/skyline/audio/adpcm_decoder.cpp
|
||||
${source_DIR}/skyline/gpu.cpp
|
||||
${source_DIR}/skyline/gpu/texture.cpp
|
||||
${source_DIR}/skyline/os.cpp
|
||||
|
54
app/src/main/cpp/skyline/audio/adpcm_decoder.cpp
Normal file
54
app/src/main/cpp/skyline/audio/adpcm_decoder.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <array>
|
||||
#include <common.h>
|
||||
#include "common.h"
|
||||
#include "adpcm_decoder.h"
|
||||
|
||||
namespace skyline::audio {
|
||||
AdpcmDecoder::AdpcmDecoder(const std::vector<std::array<i16, 2>> &coefficients) : coefficients(coefficients) {}
|
||||
|
||||
std::vector<i16> AdpcmDecoder::Decode(const std::vector<u8> &adpcmData) {
|
||||
constexpr size_t BytesPerFrame = 0x8; //!< The number of bytes in a single frame
|
||||
constexpr size_t SamplesPerFrame = 0xe; //!< The number of samples in a single frame
|
||||
|
||||
size_t remainingSamples = (adpcmData.size() / BytesPerFrame) * SamplesPerFrame;
|
||||
|
||||
std::vector<i16> output;
|
||||
output.reserve(remainingSamples);
|
||||
|
||||
size_t inputOffset{};
|
||||
|
||||
while (inputOffset < adpcmData.size()) {
|
||||
FrameHeader header(adpcmData[inputOffset++]);
|
||||
|
||||
size_t frameSamples = std::min(SamplesPerFrame, remainingSamples);
|
||||
|
||||
i32 ctx{};
|
||||
|
||||
for (size_t index = 0; index < frameSamples; index++) {
|
||||
i32 sample{};
|
||||
|
||||
if (index & 1) {
|
||||
sample = (ctx << 28) >> 28;
|
||||
} else {
|
||||
ctx = adpcmData[inputOffset++];
|
||||
sample = (ctx << 24) >> 28;
|
||||
}
|
||||
|
||||
i32 prediction = history[0] * coefficients[header.coefficientIndex][0] + history[1] * coefficients[header.coefficientIndex][1];
|
||||
sample = (sample * (0x800 << header.scale) + prediction + 0x400) >> 11;
|
||||
|
||||
auto saturated = audio::Saturate<i16, i32>(sample);
|
||||
output.push_back(saturated);
|
||||
history[1] = history[0];
|
||||
history[0] = saturated;
|
||||
}
|
||||
|
||||
remainingSamples -= frameSamples;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
42
app/src/main/cpp/skyline/audio/adpcm_decoder.h
Normal file
42
app/src/main/cpp/skyline/audio/adpcm_decoder.h
Normal file
@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline::audio {
|
||||
/**
|
||||
* @brief The AdpcmDecoder class handles decoding single channel adaptive differential PCM data
|
||||
*/
|
||||
class AdpcmDecoder {
|
||||
private:
|
||||
/**
|
||||
* @brief This struct holds a single ADPCM frame header
|
||||
*/
|
||||
union FrameHeader {
|
||||
struct {
|
||||
u8 scale : 4; //!< The scale factor for this frame
|
||||
u8 coefficientIndex : 3; //!< The index of the coeffcients to use for this frame
|
||||
u8 _pad_ :1;
|
||||
};
|
||||
u8 raw; //!< The raw value
|
||||
|
||||
FrameHeader(u8 raw) : raw(raw) {}
|
||||
};
|
||||
static_assert(sizeof(FrameHeader) == 0x1);
|
||||
|
||||
std::array<i32, 2> history{}; //!< This contains the history for decoding the ADPCM stream
|
||||
std::vector<std::array<i16, 2>> coefficients; //!< This contains the coefficients for decoding the ADPCM stream
|
||||
|
||||
public:
|
||||
AdpcmDecoder(const std::vector<std::array<i16, 2>> &coefficients);
|
||||
|
||||
/**
|
||||
* @brief This decodes a buffer of ADPCM data into I16 PCM
|
||||
* @param adpcmData A buffer containing the raw ADPCM data
|
||||
* @return A buffer containing decoded single channel I16 PCM data
|
||||
*/
|
||||
std::vector<i16> Decode(const std::vector<u8> &adpcmData);
|
||||
};
|
||||
}
|
@ -30,17 +30,24 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
return;
|
||||
|
||||
if (input.firstUpdate) {
|
||||
if (input.format != skyline::audio::AudioFormat::Int16)
|
||||
if (input.format != skyline::audio::AudioFormat::Int16 && input.format != skyline::audio::AudioFormat::ADPCM)
|
||||
throw exception("Unsupported voice PCM format: {}", input.format);
|
||||
|
||||
format = input.format;
|
||||
sampleRate = input.sampleRate;
|
||||
|
||||
if (input.channelCount > 2)
|
||||
if (input.channelCount > (input.format == skyline::audio::AudioFormat::ADPCM ? 1 : 2))
|
||||
throw exception("Unsupported voice channel count: {}", input.channelCount);
|
||||
|
||||
channelCount = static_cast<u8>(input.channelCount);
|
||||
|
||||
if (input.format == skyline::audio::AudioFormat::ADPCM) {
|
||||
std::vector<std::array<i16, 2>> adpcmCoefficients(input.adpcmCoeffsSize / (sizeof(u16) * 2));
|
||||
state.process->ReadMemory(adpcmCoefficients.data(), input.adpcmCoeffsPosition, input.adpcmCoeffsSize);
|
||||
|
||||
adpcmDecoder = skyline::audio::AdpcmDecoder(adpcmCoefficients);
|
||||
}
|
||||
|
||||
SetWaveBufferIndex(static_cast<u8>(input.baseWaveBufferIndex));
|
||||
}
|
||||
|
||||
@ -60,6 +67,12 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
samples.resize(currentBuffer.size / sizeof(i16));
|
||||
state.process->ReadMemory(samples.data(), currentBuffer.address, currentBuffer.size);
|
||||
break;
|
||||
case skyline::audio::AudioFormat::ADPCM: {
|
||||
std::vector<u8> adpcmData(currentBuffer.size);
|
||||
state.process->ReadMemory(adpcmData.data(), currentBuffer.address, currentBuffer.size);
|
||||
samples = adpcmDecoder->Decode(adpcmData);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw exception("Unsupported PCM format used by Voice: {}", format);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <audio/resampler.h>
|
||||
#include <audio/adpcm_decoder.h>
|
||||
#include <audio.h>
|
||||
#include <common.h>
|
||||
|
||||
@ -91,6 +92,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
std::array<WaveBuffer, 4> waveBuffers; //!< An array containing the state of all four wave buffers
|
||||
std::vector<i16> samples; //!< A vector containing processed sample data
|
||||
skyline::audio::Resampler resampler; //!< The resampler object used for changing the sample rate of a stream
|
||||
std::optional<skyline::audio::AdpcmDecoder> adpcmDecoder; //!< The decoder object used for decoding ADPCM encoded samples
|
||||
|
||||
bool acquired{false}; //!< If the voice is in use
|
||||
bool bufferReload{true}; //!< If the buffer needs to be updated
|
||||
|
Loading…
Reference in New Issue
Block a user