summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar MerryMage2016-02-21 13:13:52 +0000
committerGravatar MerryMage2016-02-21 13:13:52 +0000
commit8b00954ec79fad71691ad2d4c82d5c1c60e21b0c (patch)
tree443d275fd39c58928e68ef22ce3fe0fa56c73642 /src
parentMerge pull request #1406 from MerryMage/bitfield2 (diff)
downloadyuzu-8b00954ec79fad71691ad2d4c82d5c1c60e21b0c.tar.gz
yuzu-8b00954ec79fad71691ad2d4c82d5c1c60e21b0c.tar.xz
yuzu-8b00954ec79fad71691ad2d4c82d5c1c60e21b0c.zip
AudioCore: Skeleton Implementation
This commit: * Adds a new subproject, audio_core. * Defines structures that exist in DSP shared memory. * Hooks up various other parts of the emulator into audio core. This sets the foundation for a later HLE DSP implementation.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/audio_core.cpp53
-rw-r--r--src/audio_core/audio_core.h26
-rw-r--r--src/audio_core/hle/dsp.cpp42
-rw-r--r--src/audio_core/hle/dsp.h502
-rw-r--r--src/audio_core/hle/pipe.cpp55
-rw-r--r--src/audio_core/hle/pipe.h38
-rw-r--r--src/audio_core/sink.h34
-rw-r--r--src/citra/CMakeLists.txt2
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/common/bit_field.h2
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/core/hle/kernel/memory.cpp5
-rw-r--r--src/core/hle/service/dsp_dsp.cpp135
-rw-r--r--src/core/hle/service/dsp_dsp.h12
-rw-r--r--src/core/hw/gpu.cpp6
-rw-r--r--src/core/system.cpp7
19 files changed, 873 insertions, 69 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cb09f3cd1..2bb411492 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,6 +4,7 @@ include_directories(.)
4add_subdirectory(common) 4add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(video_core) 6add_subdirectory(video_core)
7add_subdirectory(audio_core)
7if (ENABLE_GLFW) 8if (ENABLE_GLFW)
8 add_subdirectory(citra) 9 add_subdirectory(citra)
9endif() 10endif()
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..b0d1c7eb6
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,16 @@
1set(SRCS
2 audio_core.cpp
3 hle/dsp.cpp
4 hle/pipe.cpp
5 )
6
7set(HEADERS
8 audio_core.h
9 hle/dsp.h
10 hle/pipe.h
11 sink.h
12 )
13
14create_directory_groups(${SRCS} ${HEADERS})
15
16add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
new file mode 100644
index 000000000..894f46990
--- /dev/null
+++ b/src/audio_core/audio_core.cpp
@@ -0,0 +1,53 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_core.h"
6#include "audio_core/hle/dsp.h"
7
8#include "core/core_timing.h"
9#include "core/hle/kernel/vm_manager.h"
10#include "core/hle/service/dsp_dsp.h"
11
12namespace AudioCore {
13
14// Audio Ticks occur about every 5 miliseconds.
15static int tick_event; ///< CoreTiming event
16static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
17
18static void AudioTickCallback(u64 /*userdata*/, int cycles_late) {
19 if (DSP::HLE::Tick()) {
20 // HACK: We're not signaling the interrups when they should be, but just firing them all off together.
21 // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here.
22 // TODO(merry): Understand when the other interrupts are fired.
23 DSP_DSP::SignalAllInterrupts();
24 }
25
26 // Reschedule recurrent event
27 CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
28}
29
30/// Initialise Audio
31void Init() {
32 DSP::HLE::Init();
33
34 tick_event = CoreTiming::RegisterEvent("AudioCore::tick_event", AudioTickCallback);
35 CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
36}
37
38/// Add DSP address spaces to Process's address space.
39void AddAddressSpace(Kernel::VMManager& address_space) {
40 auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_region0), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
41 address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
42
43 auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_region1), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
44 address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
45}
46
47/// Shutdown Audio
48void Shutdown() {
49 CoreTiming::UnscheduleEvent(tick_event, 0);
50 DSP::HLE::Shutdown();
51}
52
53} //namespace
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
new file mode 100644
index 000000000..64c330914
--- /dev/null
+++ b/src/audio_core/audio_core.h
@@ -0,0 +1,26 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Kernel {
8class VMManager;
9}
10
11namespace AudioCore {
12
13constexpr int num_sources = 24;
14constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
15constexpr int native_sample_rate = 32728; ///< 32kHz
16
17/// Initialise Audio Core
18void Init();
19
20/// Add DSP address spaces to a Process.
21void AddAddressSpace(Kernel::VMManager& vm_manager);
22
23/// Shutdown Audio Core
24void Shutdown();
25
26} // namespace
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
new file mode 100644
index 000000000..c89356edc
--- /dev/null
+++ b/src/audio_core/hle/dsp.cpp
@@ -0,0 +1,42 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/hle/dsp.h"
6#include "audio_core/hle/pipe.h"
7
8namespace DSP {
9namespace HLE {
10
11SharedMemory g_region0;
12SharedMemory g_region1;
13
14void Init() {
15 DSP::HLE::ResetPipes();
16}
17
18void Shutdown() {
19}
20
21bool Tick() {
22 return true;
23}
24
25SharedMemory& CurrentRegion() {
26 // The region with the higher frame counter is chosen unless there is wraparound.
27
28 if (g_region0.frame_counter == 0xFFFFu && g_region1.frame_counter != 0xFFFEu) {
29 // Wraparound has occured.
30 return g_region1;
31 }
32
33 if (g_region1.frame_counter == 0xFFFFu && g_region0.frame_counter != 0xFFFEu) {
34 // Wraparound has occured.
35 return g_region0;
36 }
37
38 return (g_region0.frame_counter > g_region1.frame_counter) ? g_region0 : g_region1;
39}
40
41} // namespace HLE
42} // namespace DSP
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
new file mode 100644
index 000000000..14c4000c6
--- /dev/null
+++ b/src/audio_core/hle/dsp.h
@@ -0,0 +1,502 @@
1// Copyright 2016 Citra 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 <cstddef>
8#include <type_traits>
9
10#include "audio_core/audio_core.h"
11
12#include "common/bit_field.h"
13#include "common/common_funcs.h"
14#include "common/common_types.h"
15#include "common/swap.h"
16
17namespace DSP {
18namespace HLE {
19
20// The application-accessible region of DSP memory consists of two parts.
21// Both are marked as IO and have Read/Write permissions.
22//
23// First Region: 0x1FF50000 (Size: 0x8000)
24// Second Region: 0x1FF70000 (Size: 0x8000)
25//
26// The DSP reads from each region alternately based on the frame counter for each region much like a
27// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
28// each audio tick.
29
30struct SharedMemory;
31
32constexpr VAddr region0_base = 0x1FF50000;
33extern SharedMemory g_region0;
34
35constexpr VAddr region1_base = 0x1FF70000;
36extern SharedMemory g_region1;
37
38/**
39 * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
40 * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
41 * layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
42 * middle-endian.
43 *
44 * Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
45 * sensible choice of keeping that little-endian. There are also some exceptions such as the
46 * IntermediateMixSamples structure, which is little-endian.
47 *
48 * This struct implements the conversion to and from this middle-endianness.
49 */
50struct u32_dsp {
51 u32_dsp() = default;
52 operator u32() const {
53 return Convert(storage);
54 }
55 void operator=(u32 new_value) {
56 storage = Convert(new_value);
57 }
58private:
59 static constexpr u32 Convert(u32 value) {
60 return (value << 16) | (value >> 16);
61 }
62 u32_le storage;
63};
64#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
65static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
66#endif
67
68// There are 15 structures in each memory region. A table of them in the order they appear in memory
69// is presented below
70//
71// Pipe 2 # First Region DSP Address Purpose Control
72// 5 0x8400 DSP Status DSP
73// 9 0x8410 DSP Debug Info DSP
74// 6 0x8540 Final Mix Samples DSP
75// 2 0x8680 Source Status [24] DSP
76// 8 0x8710 Compressor Table Application
77// 4 0x9430 DSP Configuration Application
78// 7 0x9492 Intermediate Mix Samples DSP + App
79// 1 0x9E92 Source Configuration [24] Application
80// 3 0xA792 Source ADPCM Coefficients [24] Application
81// 10 0xA912 Surround Sound Related
82// 11 0xAA12 Surround Sound Related
83// 12 0xAAD2 Surround Sound Related
84// 13 0xAC52 Surround Sound Related
85// 14 0xAC5C Surround Sound Related
86// 0 0xBFFF Frame Counter Application
87//
88// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
89// not fixed in stone. The addresses above are only an examplar; they're what this implementation
90// does and provides to applications.
91//
92// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
93// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
94// second region via:
95// second_region_dsp_addr = first_region_dsp_addr | 0x10000
96//
97// Applications maintain most of its own audio state, the memory region is used mainly for
98// communication and not storage of state.
99//
100// In the documentation below, filter and effect transfer functions are specified in the z domain.
101// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
102// frequency domain, just like how the s domain is the analog frequency domain.)
103
104#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
105
106// GCC versions < 5.0 do not implement std::is_trivially_copyable.
107// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
108#if (__GNUC__ >= 5) || defined(__clang__)
109 #define ASSERT_DSP_STRUCT(name, size) \
110 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
111 static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
112 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
113#else
114 #define ASSERT_DSP_STRUCT(name, size) \
115 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
116 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
117#endif
118
119struct SourceConfiguration {
120 struct Configuration {
121 /// These dirty flags are set by the application when it updates the fields in this struct.
122 /// The DSP clears these each audio frame.
123 union {
124 u32_le dirty_raw;
125
126 BitField<2, 1, u32_le> adpcm_coefficients_dirty;
127 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
128
129 BitField<16, 1, u32_le> enable_dirty;
130 BitField<17, 1, u32_le> interpolation_dirty;
131 BitField<18, 1, u32_le> rate_multiplier_dirty;
132 BitField<19, 1, u32_le> buffer_queue_dirty;
133 BitField<20, 1, u32_le> loop_related_dirty;
134 BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
135 BitField<22, 1, u32_le> filters_enabled_dirty;
136 BitField<23, 1, u32_le> simple_filter_dirty;
137 BitField<24, 1, u32_le> biquad_filter_dirty;
138 BitField<25, 1, u32_le> gain_0_dirty;
139 BitField<26, 1, u32_le> gain_1_dirty;
140 BitField<27, 1, u32_le> gain_2_dirty;
141 BitField<28, 1, u32_le> sync_dirty;
142 BitField<29, 1, u32_le> reset_flag;
143
144 BitField<31, 1, u32_le> embedded_buffer_dirty;
145 };
146
147 // Gain control
148
149 /**
150 * Gain is between 0.0-1.0. This determines how much will this source appear on
151 * each of the 12 channels that feed into the intermediate mixers.
152 * Each of the three intermediate mixers is fed two left and two right channels.
153 */
154 float_le gain[3][4];
155
156 // Interpolation
157
158 /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
159 float_le rate_multiplier;
160
161 enum class InterpolationMode : u8 {
162 None = 0,
163 Linear = 1,
164 Polyphase = 2
165 };
166
167 InterpolationMode interpolation_mode;
168 INSERT_PADDING_BYTES(1); ///< Interpolation related
169
170 // Filters
171
172 /**
173 * This is the simplest normalized first-order digital recursive filter.
174 * The transfer function of this filter is:
175 * H(z) = b0 / (1 + a1 z^-1)
176 * Values are signed fixed point with 15 fractional bits.
177 */
178 struct SimpleFilter {
179 s16_le b0;
180 s16_le a1;
181 };
182
183 /**
184 * This is a normalised biquad filter (second-order).
185 * The transfer function of this filter is:
186 * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
187 * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation
188 * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
189 * Values are signed fixed point with 14 fractional bits.
190 */
191 struct BiquadFilter {
192 s16_le b0;
193 s16_le b1;
194 s16_le b2;
195 s16_le a1;
196 s16_le a2;
197 };
198
199 union {
200 u16_le filters_enabled;
201 BitField<0, 1, u16_le> simple_filter_enabled;
202 BitField<1, 1, u16_le> biquad_filter_enabled;
203 };
204
205 SimpleFilter simple_filter;
206 BiquadFilter biquad_filter;
207
208 // Buffer Queue
209
210 /// A buffer of audio data from the application, along with metadata about it.
211 struct Buffer {
212 /// Physical memory address of the start of the buffer
213 u32_dsp physical_address;
214
215 /// This is length in terms of samples.
216 /// Note that in different buffer formats a sample takes up different number of bytes.
217 u32_dsp length;
218
219 /// ADPCM Predictor (4 bits) and Scale (4 bits)
220 union {
221 u16_le adpcm_ps;
222 BitField<0, 4, u16_le> adpcm_scale;
223 BitField<4, 4, u16_le> adpcm_predictor;
224 };
225
226 /// ADPCM Historical Samples (y[n-1] and y[n-2])
227 u16_le adpcm_yn[2];
228
229 /// This is non-zero when the ADPCM values above are to be updated.
230 u8 adpcm_dirty;
231
232 /// Is a looping buffer.
233 u8 is_looping;
234
235 /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
236 /// This allows the emulated application to tell what buffer is currently playing
237 u16_le buffer_id;
238
239 INSERT_PADDING_DSPWORDS(1);
240 };
241
242 u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
243 Buffer buffers[4]; ///< Queued Buffers
244
245 // Playback controls
246
247 u32_dsp loop_related;
248 u8 enable;
249 INSERT_PADDING_BYTES(1);
250 u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
251 u32_dsp play_position; ///< Position. (Units: number of samples)
252 INSERT_PADDING_DSPWORDS(2);
253
254 // Embedded Buffer
255 // This buffer is often the first buffer to be used when initiating audio playback,
256 // after which the buffer queue is used.
257
258 u32_dsp physical_address;
259
260 /// This is length in terms of samples.
261 /// Note a sample takes up different number of bytes in different buffer formats.
262 u32_dsp length;
263
264 enum class MonoOrStereo : u16_le {
265 Mono = 1,
266 Stereo = 2
267 };
268
269 enum class Format : u16_le {
270 PCM8 = 0,
271 PCM16 = 1,
272 ADPCM = 2
273 };
274
275 union {
276 u16_le flags1_raw;
277 BitField<0, 2, MonoOrStereo> mono_or_stereo;
278 BitField<2, 2, Format> format;
279 BitField<5, 1, u16_le> fade_in;
280 };
281
282 /// ADPCM Predictor (4 bit) and Scale (4 bit)
283 union {
284 u16_le adpcm_ps;
285 BitField<0, 4, u16_le> adpcm_scale;
286 BitField<4, 4, u16_le> adpcm_predictor;
287 };
288
289 /// ADPCM Historical Samples (y[n-1] and y[n-2])
290 u16_le adpcm_yn[2];
291
292 union {
293 u16_le flags2_raw;
294 BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
295 BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
296 };
297
298 /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer).
299 u16_le buffer_id;
300 };
301
302 Configuration config[AudioCore::num_sources];
303};
304ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
305ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
306
307struct SourceStatus {
308 struct Status {
309 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
310 u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes
311 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
312 u32_dsp buffer_position; ///< Number of samples into the current buffer
313 u16_le previous_buffer_id; ///< Updated when a buffer finishes playing
314 INSERT_PADDING_DSPWORDS(1);
315 };
316
317 Status status[AudioCore::num_sources];
318};
319ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
320
321struct DspConfiguration {
322 /// These dirty flags are set by the application when it updates the fields in this struct.
323 /// The DSP clears these each audio frame.
324 union {
325 u32_le dirty_raw;
326
327 BitField<8, 1, u32_le> mixer1_enabled_dirty;
328 BitField<9, 1, u32_le> mixer2_enabled_dirty;
329 BitField<10, 1, u32_le> delay_effect_0_dirty;
330 BitField<11, 1, u32_le> delay_effect_1_dirty;
331 BitField<12, 1, u32_le> reverb_effect_0_dirty;
332 BitField<13, 1, u32_le> reverb_effect_1_dirty;
333
334 BitField<16, 1, u32_le> volume_0_dirty;
335
336 BitField<24, 1, u32_le> volume_1_dirty;
337 BitField<25, 1, u32_le> volume_2_dirty;
338 BitField<26, 1, u32_le> output_format_dirty;
339 BitField<27, 1, u32_le> limiter_enabled_dirty;
340 BitField<28, 1, u32_le> headphones_connected_dirty;
341 };
342
343 /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
344 float_le volume[3];
345
346 INSERT_PADDING_DSPWORDS(3);
347
348 enum class OutputFormat : u16_le {
349 Mono = 0,
350 Stereo = 1,
351 Surround = 2
352 };
353
354 OutputFormat output_format;
355
356 u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
357 u16_le headphones_connected; ///< Application updates the DSP on headphone status.
358 INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
359 INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
360 u16_le mixer1_enabled;
361 u16_le mixer2_enabled;
362
363 /**
364 * This is delay with feedback.
365 * Transfer function:
366 * H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
367 * where
368 * N = frame_count * samples_per_frame
369 * g, a and b are fixed point with 7 fractional bits
370 */
371 struct DelayEffect {
372 /// These dirty flags are set by the application when it updates the fields in this struct.
373 /// The DSP clears these each audio frame.
374 union {
375 u16_le dirty_raw;
376 BitField<0, 1, u16_le> enable_dirty;
377 BitField<1, 1, u16_le> work_buffer_address_dirty;
378 BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed
379 };
380
381 u16_le enable;
382 INSERT_PADDING_DSPWORDS(1);
383 u16_le outputs;
384 u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
385 u16_le frame_count; ///< Frames to delay by
386
387 // Coefficients
388 s16_le g; ///< Fixed point with 7 fractional bits
389 s16_le a; ///< Fixed point with 7 fractional bits
390 s16_le b; ///< Fixed point with 7 fractional bits
391 };
392
393 DelayEffect delay_effect[2];
394
395 struct ReverbEffect {
396 INSERT_PADDING_DSPWORDS(26); ///< TODO
397 };
398
399 ReverbEffect reverb_effect[2];
400
401 INSERT_PADDING_DSPWORDS(4);
402};
403ASSERT_DSP_STRUCT(DspConfiguration, 196);
404ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
405ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
406
407struct AdpcmCoefficients {
408 /// Coefficients are signed fixed point with 11 fractional bits.
409 /// Each source has 16 coefficients associated with it.
410 s16_le coeff[AudioCore::num_sources][16];
411};
412ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
413
414struct DspStatus {
415 u16_le unknown;
416 u16_le dropped_frames;
417 INSERT_PADDING_DSPWORDS(0xE);
418};
419ASSERT_DSP_STRUCT(DspStatus, 32);
420
421/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
422/// When the application writes to this region it has no effect.
423struct FinalMixSamples {
424 s16_le pcm16[2 * AudioCore::samples_per_frame];
425};
426ASSERT_DSP_STRUCT(FinalMixSamples, 640);
427
428/// DSP writes output of intermediate mixers 1 and 2 here.
429/// Writes to this region by the application edits the output of the intermediate mixers.
430/// This seems to be intended to allow the application to do custom effects on the ARM11.
431/// Values that exceed s16 range will be clipped by the DSP after further processing.
432struct IntermediateMixSamples {
433 struct Samples {
434 s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
435 };
436
437 Samples mix1;
438 Samples mix2;
439};
440ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
441
442/// Compressor table
443struct Compressor {
444 INSERT_PADDING_DSPWORDS(0xD20); ///< TODO
445};
446
447/// There is no easy way to implement this in a HLE implementation.
448struct DspDebug {
449 INSERT_PADDING_DSPWORDS(0x130);
450};
451ASSERT_DSP_STRUCT(DspDebug, 0x260);
452
453struct SharedMemory {
454 /// Padding
455 INSERT_PADDING_DSPWORDS(0x400);
456
457 DspStatus dsp_status;
458
459 DspDebug dsp_debug;
460
461 FinalMixSamples final_samples;
462
463 SourceStatus source_statuses;
464
465 Compressor compressor;
466
467 DspConfiguration dsp_configuration;
468
469 IntermediateMixSamples intermediate_mix_samples;
470
471 SourceConfiguration source_configurations;
472
473 AdpcmCoefficients adpcm_coefficients;
474
475 /// Unknown 10-14 (Surround sound related)
476 INSERT_PADDING_DSPWORDS(0x16ED);
477
478 u16_le frame_counter;
479};
480ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
481
482#undef INSERT_PADDING_DSPWORDS
483#undef ASSERT_DSP_STRUCT
484
485/// Initialize DSP hardware
486void Init();
487
488/// Shutdown DSP hardware
489void Shutdown();
490
491/**
492 * Perform processing and updates state of current shared memory buffer.
493 * This function is called every audio tick before triggering the audio interrupt.
494 * @return Whether an audio interrupt should be triggered this frame.
495 */
496bool Tick();
497
498/// Returns a mutable reference to the current region. Current region is selected based on the frame counter.
499SharedMemory& CurrentRegion();
500
501} // namespace HLE
502} // namespace DSP
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
new file mode 100644
index 000000000..6542c760c
--- /dev/null
+++ b/src/audio_core/hle/pipe.cpp
@@ -0,0 +1,55 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <vector>
7
8#include "audio_core/hle/pipe.h"
9
10#include "common/common_types.h"
11#include "common/logging/log.h"
12
13namespace DSP {
14namespace HLE {
15
16static size_t pipe2position = 0;
17
18void ResetPipes() {
19 pipe2position = 0;
20}
21
22std::vector<u8> PipeRead(u32 pipe_number, u32 length) {
23 if (pipe_number != 2) {
24 LOG_WARNING(Audio_DSP, "pipe_number = %u (!= 2), unimplemented", pipe_number);
25 return {}; // We currently don't handle anything other than the audio pipe.
26 }
27
28 // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
29 // TODO: Our implementation will actually use a slightly different response than this one.
30 // TODO: Use offsetof on DSP structures instead for a proper response.
31 static const std::array<u8, 32> canned_response {{
32 0x0F, 0x00, 0xFF, 0xBF, 0x8E, 0x9E, 0x80, 0x86, 0x8E, 0xA7, 0x30, 0x94, 0x00, 0x84, 0x40, 0x85,
33 0x8E, 0x94, 0x10, 0x87, 0x10, 0x84, 0x0E, 0xA9, 0x0E, 0xAA, 0xCE, 0xAA, 0x4E, 0xAC, 0x58, 0xAC
34 }};
35
36 // TODO: Move this into dsp::DSP service since it happens on the service side.
37 // Hardware observation: No data is returned if requested length reads beyond the end of the data in-pipe.
38 if (pipe2position + length > canned_response.size()) {
39 return {};
40 }
41
42 std::vector<u8> ret;
43 for (size_t i = 0; i < length; i++, pipe2position++) {
44 ret.emplace_back(canned_response[pipe2position]);
45 }
46
47 return ret;
48}
49
50void PipeWrite(u32 pipe_number, const std::vector<u8>& buffer) {
51 // TODO: proper pipe behaviour
52}
53
54} // namespace HLE
55} // namespace DSP
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
new file mode 100644
index 000000000..ff6536950
--- /dev/null
+++ b/src/audio_core/hle/pipe.h
@@ -0,0 +1,38 @@
1// Copyright 2016 Citra 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 <vector>
8
9#include "common/common_types.h"
10
11namespace DSP {
12namespace HLE {
13
14/// Reset the pipes by setting pipe positions back to the beginning.
15void ResetPipes();
16
17/**
18 * Read a DSP pipe.
19 * Pipe IDs:
20 * pipe_number = 0: Debug
21 * pipe_number = 1: P-DMA
22 * pipe_number = 2: Audio
23 * pipe_number = 3: Binary
24 * @param pipe_number The Pipe ID
25 * @param length How much data to request.
26 * @return The data read from the pipe. The size of this vector can be less than the length requested.
27 */
28std::vector<u8> PipeRead(u32 pipe_number, u32 length);
29
30/**
31 * Write to a DSP pipe.
32 * @param pipe_number The Pipe ID
33 * @param buffer The data to write to the pipe.
34 */
35void PipeWrite(u32 pipe_number, const std::vector<u8>& buffer);
36
37} // namespace HLE
38} // namespace DSP
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
new file mode 100644
index 000000000..cad21a85e
--- /dev/null
+++ b/src/audio_core/sink.h
@@ -0,0 +1,34 @@
1// Copyright 2016 Citra 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 <vector>
8
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13/**
14 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output.
15 * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs.
16 */
17class Sink {
18public:
19 virtual ~Sink() = default;
20
21 /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
22 virtual unsigned GetNativeSampleRate() const = 0;
23
24 /**
25 * Feed stereo samples to sink.
26 * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
27 */
28 virtual void EnqueueSamples(const std::vector<s16>& samples) = 0;
29
30 /// Samples enqueued that have not been played yet.
31 virtual std::size_t SamplesInQueue() const = 0;
32};
33
34} // namespace
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index e7f8a17f9..b9abb818e 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -17,7 +17,7 @@ include_directories(${GLFW_INCLUDE_DIRS})
17link_directories(${GLFW_LIBRARY_DIRS}) 17link_directories(${GLFW_LIBRARY_DIRS})
18 18
19add_executable(citra ${SRCS} ${HEADERS}) 19add_executable(citra ${SRCS} ${HEADERS})
20target_link_libraries(citra core video_core common) 20target_link_libraries(citra core video_core audio_core common)
21target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad) 21target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
22if (MSVC) 22if (MSVC)
23 target_link_libraries(citra getopt) 23 target_link_libraries(citra getopt)
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index bbf6ae001..b3d1205a4 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -79,7 +79,7 @@ if (APPLE)
79else() 79else()
80 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) 80 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
81endif() 81endif()
82target_link_libraries(citra-qt core video_core common qhexedit) 82target_link_libraries(citra-qt core video_core audio_core common qhexedit)
83target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) 83target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
84target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) 84target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
85 85
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 600e0c70c..371eb17a1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -185,6 +185,6 @@ private:
185}; 185};
186#pragma pack() 186#pragma pack()
187 187
188#if (__GNUC__ >= 5) || defined __clang__ || defined _MSC_VER 188#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
189static_assert(std::is_trivially_copyable<BitField<0, 1, u32>>::value, "BitField must be trivially copyable"); 189static_assert(std::is_trivially_copyable<BitField<0, 1, u32>>::value, "BitField must be trivially copyable");
190#endif 190#endif
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d186ba8f8..58819012d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -58,6 +58,8 @@ namespace Log {
58 CLS(Render) \ 58 CLS(Render) \
59 SUB(Render, Software) \ 59 SUB(Render, Software) \
60 SUB(Render, OpenGL) \ 60 SUB(Render, OpenGL) \
61 CLS(Audio) \
62 SUB(Audio, DSP) \
61 CLS(Loader) 63 CLS(Loader)
62 64
63// GetClassName is a macro defined by Windows.h, grrr... 65// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 2d9323a7b..ec7bb00b8 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -73,6 +73,8 @@ enum class Class : ClassType {
73 Render, ///< Emulator video output and hardware acceleration 73 Render, ///< Emulator video output and hardware acceleration
74 Render_Software, ///< Software renderer backend 74 Render_Software, ///< Software renderer backend
75 Render_OpenGL, ///< OpenGL backend 75 Render_OpenGL, ///< OpenGL backend
76 Audio, ///< Emulator audio output
77 Audio_DSP, ///< The HLE implementation of the DSP
76 Loader, ///< ROM loader 78 Loader, ///< ROM loader
77 79
78 Count ///< Total number of logging classes 80 Count ///< Total number of logging classes
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 0cfb43fc7..862643448 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -7,6 +7,8 @@
7#include <utility> 7#include <utility>
8#include <vector> 8#include <vector>
9 9
10#include "audio_core/audio_core.h"
11
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/logging/log.h" 13#include "common/logging/log.h"
12 14
@@ -107,7 +109,6 @@ struct MemoryArea {
107static MemoryArea memory_areas[] = { 109static MemoryArea memory_areas[] = {
108 {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory 110 {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
109 {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM) 111 {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
110 {DSP_RAM_VADDR, DSP_RAM_SIZE, "DSP RAM"}, // DSP memory
111 {TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory 112 {TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
112}; 113};
113 114
@@ -133,6 +134,8 @@ void InitLegacyAddressSpace(Kernel::VMManager& address_space) {
133 auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, 134 auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR,
134 (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); 135 (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom();
135 address_space.Reprotect(shared_page_vma, VMAPermission::Read); 136 address_space.Reprotect(shared_page_vma, VMAPermission::Read);
137
138 AudioCore::AddAddressSpace(address_space);
136} 139}
137 140
138} // namespace 141} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index f9f931f6d..15d3274ec 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "audio_core/hle/pipe.h"
6
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6 8
7#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
@@ -14,17 +16,30 @@ namespace DSP_DSP {
14 16
15static u32 read_pipe_count; 17static u32 read_pipe_count;
16static Kernel::SharedPtr<Kernel::Event> semaphore_event; 18static Kernel::SharedPtr<Kernel::Event> semaphore_event;
17static Kernel::SharedPtr<Kernel::Event> interrupt_event;
18 19
19void SignalInterrupt() { 20struct PairHash {
20 // TODO(bunnei): This is just a stub, it does not do anything other than signal to the emulated 21 template <typename T, typename U>
21 // application that a DSP interrupt occurred, without specifying which one. Since we do not 22 std::size_t operator()(const std::pair<T, U> &x) const {
22 // emulate the DSP yet (and how it works is largely unknown), this is a work around to get games 23 // TODO(yuriks): Replace with better hash combining function.
23 // that check the DSP interrupt signal event to run. We should figure out the different types of 24 return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
24 // DSP interrupts, and trigger them at the appropriate times. 25 }
26};
27
28/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
29static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events;
30
31// DSP Interrupts:
32// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting
33// for an interrupt event. Immediately after this interrupt event, userland normally updates the
34// state in the next region and increments the relevant frame counter by two.
35void SignalAllInterrupts() {
36 // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case.
37 for (auto& interrupt_event : interrupt_events)
38 interrupt_event.second->Signal();
39}
25 40
26 if (interrupt_event != 0) 41void SignalInterrupt(u32 interrupt, u32 channel) {
27 interrupt_event->Signal(); 42 interrupt_events[std::make_pair(interrupt, channel)]->Signal();
28} 43}
29 44
30/** 45/**
@@ -43,7 +58,7 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
43 cmd_buff[1] = 0; // No error 58 cmd_buff[1] = 0; // No error
44 cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); 59 cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000);
45 60
46 LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr); 61 LOG_TRACE(Service_DSP, "addr=0x%08X", addr);
47} 62}
48 63
49/** 64/**
@@ -121,8 +136,8 @@ static void FlushDataCache(Service::Interface* self) {
121/** 136/**
122 * DSP_DSP::RegisterInterruptEvents service function 137 * DSP_DSP::RegisterInterruptEvents service function
123 * Inputs: 138 * Inputs:
124 * 1 : Parameter 0 (purpose unknown) 139 * 1 : Interrupt Number
125 * 2 : Parameter 1 (purpose unknown) 140 * 2 : Channel Number
126 * 4 : Interrupt event handle 141 * 4 : Interrupt event handle
127 * Outputs: 142 * Outputs:
128 * 1 : Result of function, 0 on success, otherwise error code 143 * 1 : Result of function, 0 on success, otherwise error code
@@ -130,22 +145,24 @@ static void FlushDataCache(Service::Interface* self) {
130static void RegisterInterruptEvents(Service::Interface* self) { 145static void RegisterInterruptEvents(Service::Interface* self) {
131 u32* cmd_buff = Kernel::GetCommandBuffer(); 146 u32* cmd_buff = Kernel::GetCommandBuffer();
132 147
133 u32 param0 = cmd_buff[1]; 148 u32 interrupt = cmd_buff[1];
134 u32 param1 = cmd_buff[2]; 149 u32 channel = cmd_buff[2];
135 u32 event_handle = cmd_buff[4]; 150 u32 event_handle = cmd_buff[4];
136 151
137 auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); 152 if (event_handle) {
138 if (evt != nullptr) { 153 auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
139 interrupt_event = evt; 154 if (evt) {
140 cmd_buff[1] = 0; // No error 155 interrupt_events[std::make_pair(interrupt, channel)] = evt;
156 cmd_buff[1] = RESULT_SUCCESS.raw;
157 LOG_WARNING(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
158 } else {
159 cmd_buff[1] = -1;
160 LOG_ERROR(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
161 }
141 } else { 162 } else {
142 LOG_ERROR(Service_DSP, "called with invalid handle=%08X", cmd_buff[4]); 163 interrupt_events.erase(std::make_pair(interrupt, channel));
143 164 LOG_WARNING(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
144 // TODO(yuriks): An error should be returned from SendSyncRequest, not in the cmdbuf
145 cmd_buff[1] = -1;
146 } 165 }
147
148 LOG_WARNING(Service_DSP, "(STUBBED) called param0=%u, param1=%u, event_handle=0x%08X", param0, param1, event_handle);
149} 166}
150 167
151/** 168/**
@@ -158,8 +175,6 @@ static void RegisterInterruptEvents(Service::Interface* self) {
158static void SetSemaphore(Service::Interface* self) { 175static void SetSemaphore(Service::Interface* self) {
159 u32* cmd_buff = Kernel::GetCommandBuffer(); 176 u32* cmd_buff = Kernel::GetCommandBuffer();
160 177
161 SignalInterrupt();
162
163 cmd_buff[1] = 0; // No error 178 cmd_buff[1] = 0; // No error
164 179
165 LOG_WARNING(Service_DSP, "(STUBBED) called"); 180 LOG_WARNING(Service_DSP, "(STUBBED) called");
@@ -168,9 +183,9 @@ static void SetSemaphore(Service::Interface* self) {
168/** 183/**
169 * DSP_DSP::WriteProcessPipe service function 184 * DSP_DSP::WriteProcessPipe service function
170 * Inputs: 185 * Inputs:
171 * 1 : Number 186 * 1 : Channel
172 * 2 : Size 187 * 2 : Size
173 * 3 : (size <<14) | 0x402 188 * 3 : (size << 14) | 0x402
174 * 4 : Buffer 189 * 4 : Buffer
175 * Outputs: 190 * Outputs:
176 * 0 : Return header 191 * 0 : Return header
@@ -179,21 +194,42 @@ static void SetSemaphore(Service::Interface* self) {
179static void WriteProcessPipe(Service::Interface* self) { 194static void WriteProcessPipe(Service::Interface* self) {
180 u32* cmd_buff = Kernel::GetCommandBuffer(); 195 u32* cmd_buff = Kernel::GetCommandBuffer();
181 196
182 u32 number = cmd_buff[1]; 197 u32 channel = cmd_buff[1];
183 u32 size = cmd_buff[2]; 198 u32 size = cmd_buff[2];
184 u32 new_size = cmd_buff[3];
185 u32 buffer = cmd_buff[4]; 199 u32 buffer = cmd_buff[4];
186 200
201 if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) {
202 LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). channel=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], channel, size, buffer);
203 cmd_buff[1] = -1; // TODO
204 return;
205 }
206
207 if (!Memory::GetPointer(buffer)) {
208 LOG_ERROR(Service_DSP, "Invalid Buffer: channel=%u, size=0x%X, buffer=0x%08X", channel, size, buffer);
209 cmd_buff[1] = -1; // TODO
210 return;
211 }
212
213 std::vector<u8> message(size);
214
215 for (size_t i = 0; i < size; i++) {
216 message[i] = Memory::Read8(buffer + i);
217 }
218
219 DSP::HLE::PipeWrite(channel, message);
220
187 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 221 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
188 222
189 LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%X, new_size=0x%X, buffer=0x%08X", 223 LOG_TRACE(Service_DSP, "channel=%u, size=0x%X, buffer=0x%08X", channel, size, buffer);
190 number, size, new_size, buffer);
191} 224}
192 225
193/** 226/**
194 * DSP_DSP::ReadPipeIfPossible service function 227 * DSP_DSP::ReadPipeIfPossible service function
228 * A pipe is a means of communication between the ARM11 and DSP that occurs on
229 * hardware by writing to/reading from the DSP registers at 0x10203000.
230 * Pipes are used for initialisation. See also DSP::HLE::PipeRead.
195 * Inputs: 231 * Inputs:
196 * 1 : Unknown 232 * 1 : Pipe Number
197 * 2 : Unknown 233 * 2 : Unknown
198 * 3 : Size in bytes of read (observed only lower half word used) 234 * 3 : Size in bytes of read (observed only lower half word used)
199 * 0x41 : Virtual address to read from DSP pipe to in memory 235 * 0x41 : Virtual address to read from DSP pipe to in memory
@@ -204,35 +240,25 @@ static void WriteProcessPipe(Service::Interface* self) {
204static void ReadPipeIfPossible(Service::Interface* self) { 240static void ReadPipeIfPossible(Service::Interface* self) {
205 u32* cmd_buff = Kernel::GetCommandBuffer(); 241 u32* cmd_buff = Kernel::GetCommandBuffer();
206 242
207 u32 unk1 = cmd_buff[1]; 243 u32 pipe = cmd_buff[1];
208 u32 unk2 = cmd_buff[2]; 244 u32 unk2 = cmd_buff[2];
209 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size 245 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
210 VAddr addr = cmd_buff[0x41]; 246 VAddr addr = cmd_buff[0x41];
211 247
212 // Canned DSP responses that games expect. These were taken from HW by 3dmoo team. 248 if (!Memory::GetPointer(addr)) {
213 // TODO: Remove this hack :) 249 LOG_ERROR(Service_DSP, "Invalid addr: pipe=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", pipe, unk2, size, addr);
214 static const std::array<u16, 16> canned_read_pipe = {{ 250 cmd_buff[1] = -1; // TODO
215 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540, 251 return;
216 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58 252 }
217 }};
218 253
219 u32 initial_size = read_pipe_count; 254 std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
220 255
221 for (unsigned offset = 0; offset < size; offset += sizeof(u16)) { 256 Memory::WriteBlock(addr, response.data(), response.size());
222 if (read_pipe_count < canned_read_pipe.size()) {
223 Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
224 read_pipe_count++;
225 } else {
226 LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
227 break;
228 }
229 }
230 257
231 cmd_buff[1] = 0; // No error 258 cmd_buff[1] = 0; // No error
232 cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); 259 cmd_buff[2] = (u32)response.size();
233 260
234 LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", 261 LOG_TRACE(Service_DSP, "pipe=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", pipe, unk2, size, addr);
235 unk1, unk2, size, addr);
236} 262}
237 263
238/** 264/**
@@ -311,7 +337,6 @@ const Interface::FunctionInfo FunctionTable[] = {
311 337
312Interface::Interface() { 338Interface::Interface() {
313 semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); 339 semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
314 interrupt_event = nullptr;
315 read_pipe_count = 0; 340 read_pipe_count = 0;
316 341
317 Register(FunctionTable); 342 Register(FunctionTable);
@@ -319,7 +344,7 @@ Interface::Interface() {
319 344
320Interface::~Interface() { 345Interface::~Interface() {
321 semaphore_event = nullptr; 346 semaphore_event = nullptr;
322 interrupt_event = nullptr; 347 interrupt_events.clear();
323} 348}
324 349
325} // namespace 350} // namespace
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index b6f611db5..32b89e9bb 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -23,7 +23,15 @@ public:
23 } 23 }
24}; 24};
25 25
26/// Signals that a DSP interrupt has occurred to userland code 26/// Signal all audio related interrupts.
27void SignalInterrupt(); 27void SignalAllInterrupts();
28
29/**
30 * Signal a specific audio related interrupt based on interrupt id and channel id.
31 * @param interrupt_id The interrupt id
32 * @param channel_id The channel id
33 * The significance of various values of interrupt_id and channel_id is not yet known.
34 */
35void SignalInterrupt(u32 interrupt_id, u32 channel_id);
28 36
29} // namespace 37} // namespace
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index c60310586..5312baa83 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -17,7 +17,6 @@
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18 18
19#include "core/hle/service/gsp_gpu.h" 19#include "core/hle/service/gsp_gpu.h"
20#include "core/hle/service/dsp_dsp.h"
21#include "core/hle/service/hid/hid.h" 20#include "core/hle/service/hid/hid.h"
22 21
23#include "core/hw/hw.h" 22#include "core/hw/hw.h"
@@ -414,11 +413,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
414 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); 413 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
415 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); 414 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
416 415
417 // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
418 // until we can emulate DSP interrupts, this is probably the only reasonable place to do
419 // this. Certain games expect this to be periodically signaled.
420 DSP_DSP::SignalInterrupt();
421
422 // Check for user input updates 416 // Check for user input updates
423 Service::HID::Update(); 417 Service::HID::Update();
424 418
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 7e9c56538..b62ebf69e 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -2,9 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "audio_core/audio_core.h"
6
5#include "core/core.h" 7#include "core/core.h"
6#include "core/core_timing.h" 8#include "core/core_timing.h"
7#include "core/system.h" 9#include "core/system.h"
10#include "core/gdbstub/gdbstub.h"
8#include "core/hw/hw.h" 11#include "core/hw/hw.h"
9#include "core/hle/hle.h" 12#include "core/hle/hle.h"
10#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
@@ -12,8 +15,6 @@
12 15
13#include "video_core/video_core.h" 16#include "video_core/video_core.h"
14 17
15#include "core/gdbstub/gdbstub.h"
16
17namespace System { 18namespace System {
18 19
19void Init(EmuWindow* emu_window) { 20void Init(EmuWindow* emu_window) {
@@ -24,11 +25,13 @@ void Init(EmuWindow* emu_window) {
24 Kernel::Init(); 25 Kernel::Init();
25 HLE::Init(); 26 HLE::Init();
26 VideoCore::Init(emu_window); 27 VideoCore::Init(emu_window);
28 AudioCore::Init();
27 GDBStub::Init(); 29 GDBStub::Init();
28} 30}
29 31
30void Shutdown() { 32void Shutdown() {
31 GDBStub::Shutdown(); 33 GDBStub::Shutdown();
34 AudioCore::Shutdown();
32 VideoCore::Shutdown(); 35 VideoCore::Shutdown();
33 HLE::Shutdown(); 36 HLE::Shutdown();
34 Kernel::Shutdown(); 37 Kernel::Shutdown();