mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 14:44: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.cpp
|
||||||
${source_DIR}/skyline/audio/track.cpp
|
${source_DIR}/skyline/audio/track.cpp
|
||||||
${source_DIR}/skyline/audio/resampler.cpp
|
${source_DIR}/skyline/audio/resampler.cpp
|
||||||
|
${source_DIR}/skyline/audio/adpcm_decoder.cpp
|
||||||
${source_DIR}/skyline/gpu.cpp
|
${source_DIR}/skyline/gpu.cpp
|
||||||
${source_DIR}/skyline/gpu/texture.cpp
|
${source_DIR}/skyline/gpu/texture.cpp
|
||||||
${source_DIR}/skyline/os.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;
|
return;
|
||||||
|
|
||||||
if (input.firstUpdate) {
|
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);
|
throw exception("Unsupported voice PCM format: {}", input.format);
|
||||||
|
|
||||||
format = input.format;
|
format = input.format;
|
||||||
sampleRate = input.sampleRate;
|
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);
|
throw exception("Unsupported voice channel count: {}", input.channelCount);
|
||||||
|
|
||||||
channelCount = static_cast<u8>(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));
|
SetWaveBufferIndex(static_cast<u8>(input.baseWaveBufferIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +67,12 @@ namespace skyline::service::audio::IAudioRenderer {
|
|||||||
samples.resize(currentBuffer.size / sizeof(i16));
|
samples.resize(currentBuffer.size / sizeof(i16));
|
||||||
state.process->ReadMemory(samples.data(), currentBuffer.address, currentBuffer.size);
|
state.process->ReadMemory(samples.data(), currentBuffer.address, currentBuffer.size);
|
||||||
break;
|
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:
|
default:
|
||||||
throw exception("Unsupported PCM format used by Voice: {}", format);
|
throw exception("Unsupported PCM format used by Voice: {}", format);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <audio/resampler.h>
|
#include <audio/resampler.h>
|
||||||
|
#include <audio/adpcm_decoder.h>
|
||||||
#include <audio.h>
|
#include <audio.h>
|
||||||
#include <common.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::array<WaveBuffer, 4> waveBuffers; //!< An array containing the state of all four wave buffers
|
||||||
std::vector<i16> samples; //!< A vector containing processed sample data
|
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
|
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 acquired{false}; //!< If the voice is in use
|
||||||
bool bufferReload{true}; //!< If the buffer needs to be updated
|
bool bufferReload{true}; //!< If the buffer needs to be updated
|
||||||
|
Loading…
Reference in New Issue
Block a user