summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2018-08-03 15:30:01 -0400
committerGravatar bunnei2018-08-04 18:22:58 -0400
commitf1cb3903ac358183dcdc562ba19dc469b056e73f (patch)
tree0179ee55d3c573cdd9189cb708e2b69e02a1b166
parentcubeb_sink: Support variable sample_rate and num_channels. (diff)
downloadyuzu-f1cb3903ac358183dcdc562ba19dc469b056e73f.tar.gz
yuzu-f1cb3903ac358183dcdc562ba19dc469b056e73f.tar.xz
yuzu-f1cb3903ac358183dcdc562ba19dc469b056e73f.zip
audio_core: Port codec code from Citra for ADPCM decoding.
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/codec.cpp77
-rw-r--r--src/audio_core/codec.h44
-rw-r--r--src/core/hle/service/audio/audout_u.cpp4
-rw-r--r--src/core/hle/service/audio/audout_u.h10
5 files changed, 126 insertions, 11 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 81121167d..05a61b5cd 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -4,6 +4,8 @@ add_library(audio_core STATIC
4 buffer.h 4 buffer.h
5 cubeb_sink.cpp 5 cubeb_sink.cpp
6 cubeb_sink.h 6 cubeb_sink.h
7 codec.cpp
8 codec.h
7 null_sink.h 9 null_sink.h
8 stream.cpp 10 stream.cpp
9 stream.h 11 stream.h
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..c3021403f
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,77 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "audio_core/codec.h"
8
9namespace AudioCore::Codec {
10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
12 ADPCMState& state) {
13 // GC-ADPCM with scale factor and variable coefficients.
14 // Frames are 8 bytes long containing 14 samples each.
15 // Samples are 4 bits (one nibble) long.
16
17 constexpr size_t FRAME_LEN = 8;
18 constexpr size_t SAMPLES_PER_FRAME = 14;
19 constexpr std::array<int, 16> SIGNED_NIBBLES = {
20 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
21
22 const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
23 const size_t ret_size =
24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
25 std::vector<s16> ret(ret_size);
26
27 int yn1 = state.yn1, yn2 = state.yn2;
28
29 const size_t NUM_FRAMES =
30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
31 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
32 const int frame_header = data[framei * FRAME_LEN];
33 const int scale = 1 << (frame_header & 0xF);
34 const int idx = (frame_header >> 4) & 0x7;
35
36 // Coefficients are fixed point with 11 bits fractional part.
37 const int coef1 = coeff[idx * 2 + 0];
38 const int coef2 = coeff[idx * 2 + 1];
39
40 // Decodes an audio sample. One nibble produces one sample.
41 const auto decode_sample = [&](const int nibble) -> s16 {
42 const int xn = nibble * scale;
43 // We first transform everything into 11 bit fixed point, perform the second order
44 // digital filter, then transform back.
45 // 0x400 == 0.5 in 11 bit fixed point.
46 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
47 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
48 // Clamp to output range.
49 val = std::clamp<s32>(val, -32768, 32767);
50 // Advance output feedback.
51 yn2 = yn1;
52 yn1 = val;
53 return static_cast<s16>(val);
54 };
55
56 size_t outputi = framei * SAMPLES_PER_FRAME;
57 size_t datai = framei * FRAME_LEN + 1;
58 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
60 ret[outputi] = sample1;
61 outputi++;
62
63 const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
64 ret[outputi] = sample2;
65 outputi++;
66
67 datai++;
68 }
69 }
70
71 state.yn1 = yn1;
72 state.yn2 = yn2;
73
74 return ret;
75}
76
77} // namespace AudioCore::Codec
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..3f845c42c
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore::Codec {
13
14enum class PcmFormat : u32 {
15 Invalid = 0,
16 Int8 = 1,
17 Int16 = 2,
18 Int24 = 3,
19 Int32 = 4,
20 PcmFloat = 5,
21 Adpcm = 6,
22};
23
24/// See: Codec::DecodeADPCM
25struct ADPCMState {
26 // Two historical samples from previous processed buffer,
27 // required for ADPCM decoding
28 s16 yn1; ///< y[n-1]
29 s16 yn2; ///< y[n-2]
30};
31
32using ADPCM_Coeff = std::array<s16, 16>;
33
34/**
35 * @param data Pointer to buffer that contains ADPCM data to decode
36 * @param size Size of buffer in bytes
37 * @param coeff ADPCM coefficients
38 * @param state ADPCM state, this is updated with new state
39 * @return Decoded stereo signed PCM16 data, sample_count in length
40 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
42 ADPCMState& state);
43
44}; // namespace AudioCore::Codec
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 9f4c7855a..f4a557634 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -4,6 +4,8 @@
4 4
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7
8#include "audio_core/codec.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
200 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
201 rb.Push<u32>(DefaultSampleRate); 203 rb.Push<u32>(DefaultSampleRate);
202 rb.Push<u32>(params.channel_count); 204 rb.Push<u32>(params.channel_count);
203 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 205 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
204 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 206 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
205 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 207 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
206} 208}
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index e5c2184d5..fd491f65d 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -38,16 +38,6 @@ private:
38 38
39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
41
42 enum class PcmFormat : u32 {
43 Invalid = 0,
44 Int8 = 1,
45 Int16 = 2,
46 Int24 = 3,
47 Int32 = 4,
48 PcmFloat = 5,
49 Adpcm = 6,
50 };
51}; 41};
52 42
53} // namespace Service::Audio 43} // namespace Service::Audio