mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #925 from bunnei/audren
Implement audren audio output
This commit is contained in:
		
						commit
						bb21c2198a
					
				@ -1,9 +1,13 @@
 | 
				
			|||||||
add_library(audio_core STATIC
 | 
					add_library(audio_core STATIC
 | 
				
			||||||
    audio_out.cpp
 | 
					    audio_out.cpp
 | 
				
			||||||
    audio_out.h
 | 
					    audio_out.h
 | 
				
			||||||
 | 
					    audio_renderer.cpp
 | 
				
			||||||
 | 
					    audio_renderer.h
 | 
				
			||||||
    buffer.h
 | 
					    buffer.h
 | 
				
			||||||
    cubeb_sink.cpp
 | 
					    cubeb_sink.cpp
 | 
				
			||||||
    cubeb_sink.h
 | 
					    cubeb_sink.h
 | 
				
			||||||
 | 
					    codec.cpp
 | 
				
			||||||
 | 
					    codec.h
 | 
				
			||||||
    null_sink.h
 | 
					    null_sink.h
 | 
				
			||||||
    stream.cpp
 | 
					    stream.cpp
 | 
				
			||||||
    stream.h
 | 
					    stream.h
 | 
				
			||||||
 | 
				
			|||||||
@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
 | 
				
			|||||||
    return {};
 | 
					    return {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
 | 
					StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
 | 
				
			||||||
                               Stream::ReleaseCallback&& release_callback) {
 | 
					                               Stream::ReleaseCallback&& release_callback) {
 | 
				
			||||||
    if (!sink) {
 | 
					    if (!sink) {
 | 
				
			||||||
        const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
 | 
					        const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
 | 
				
			||||||
        sink = sink_details.factory(Settings::values.audio_device_id);
 | 
					        sink = sink_details.factory(Settings::values.audio_device_id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
 | 
					    return std::make_shared<Stream>(
 | 
				
			||||||
                                    std::move(release_callback),
 | 
					        sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
 | 
				
			||||||
                                    sink->AcquireSinkStream(sample_rate, num_channels));
 | 
					        sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
 | 
					std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
 | 
				
			||||||
@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
 | 
				
			|||||||
    stream->Stop();
 | 
					    stream->Stop();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
 | 
					bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
 | 
				
			||||||
    return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
 | 
					    return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "audio_core/buffer.h"
 | 
					#include "audio_core/buffer.h"
 | 
				
			||||||
@ -20,7 +21,7 @@ namespace AudioCore {
 | 
				
			|||||||
class AudioOut {
 | 
					class AudioOut {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    /// Opens a new audio stream
 | 
					    /// Opens a new audio stream
 | 
				
			||||||
    StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
 | 
					    StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
 | 
				
			||||||
                         Stream::ReleaseCallback&& release_callback);
 | 
					                         Stream::ReleaseCallback&& release_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns a vector of recently released buffers specified by tag for the specified stream
 | 
					    /// Returns a vector of recently released buffers specified by tag for the specified stream
 | 
				
			||||||
@ -33,7 +34,7 @@ public:
 | 
				
			|||||||
    void StopStream(StreamPtr stream);
 | 
					    void StopStream(StreamPtr stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Queues a buffer into the specified audio stream, returns true on success
 | 
					    /// Queues a buffer into the specified audio stream, returns true on success
 | 
				
			||||||
    bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
 | 
					    bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    SinkPtr sink;
 | 
					    SinkPtr sink;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										234
									
								
								src/audio_core/audio_renderer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/audio_core/audio_renderer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,234 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audio_core/audio_renderer.h"
 | 
				
			||||||
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
 | 
					#include "core/memory.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace AudioCore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr u32 STREAM_SAMPLE_RATE{48000};
 | 
				
			||||||
 | 
					constexpr u32 STREAM_NUM_CHANNELS{2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioRenderer::AudioRenderer(AudioRendererParameter params,
 | 
				
			||||||
 | 
					                             Kernel::SharedPtr<Kernel::Event> buffer_event)
 | 
				
			||||||
 | 
					    : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    audio_core = std::make_unique<AudioCore::AudioOut>();
 | 
				
			||||||
 | 
					    stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
 | 
				
			||||||
 | 
					                                    [=]() { buffer_event->Signal(); });
 | 
				
			||||||
 | 
					    audio_core->StartStream(stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QueueMixedBuffer(0);
 | 
				
			||||||
 | 
					    QueueMixedBuffer(1);
 | 
				
			||||||
 | 
					    QueueMixedBuffer(2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
 | 
				
			||||||
 | 
					    // Copy UpdateDataHeader struct
 | 
				
			||||||
 | 
					    UpdateDataHeader config{};
 | 
				
			||||||
 | 
					    std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
 | 
				
			||||||
 | 
					    u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Copy MemoryPoolInfo structs
 | 
				
			||||||
 | 
					    std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
 | 
				
			||||||
 | 
					    std::memcpy(mem_pool_info.data(),
 | 
				
			||||||
 | 
					                input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
 | 
				
			||||||
 | 
					                memory_pool_count * sizeof(MemoryPoolInfo));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Copy VoiceInfo structs
 | 
				
			||||||
 | 
					    size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
 | 
				
			||||||
 | 
					                  config.voice_resource_size};
 | 
				
			||||||
 | 
					    for (auto& voice : voices) {
 | 
				
			||||||
 | 
					        std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
 | 
				
			||||||
 | 
					        offset += sizeof(VoiceInfo);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update voices
 | 
				
			||||||
 | 
					    for (auto& voice : voices) {
 | 
				
			||||||
 | 
					        voice.UpdateState();
 | 
				
			||||||
 | 
					        if (!voice.GetInfo().is_in_use) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (voice.GetInfo().is_new) {
 | 
				
			||||||
 | 
					            voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update memory pool state
 | 
				
			||||||
 | 
					    std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
 | 
				
			||||||
 | 
					    for (size_t index = 0; index < memory_pool.size(); ++index) {
 | 
				
			||||||
 | 
					        if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
 | 
				
			||||||
 | 
					            memory_pool[index].state = MemoryPoolStates::Attached;
 | 
				
			||||||
 | 
					        } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
 | 
				
			||||||
 | 
					            memory_pool[index].state = MemoryPoolStates::Detached;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Release previous buffers and queue next ones for playback
 | 
				
			||||||
 | 
					    ReleaseAndQueueBuffers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Copy output header
 | 
				
			||||||
 | 
					    UpdateDataHeader response_data{worker_params};
 | 
				
			||||||
 | 
					    std::vector<u8> output_params(response_data.total_size);
 | 
				
			||||||
 | 
					    std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Copy output memory pool entries
 | 
				
			||||||
 | 
					    std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
 | 
				
			||||||
 | 
					                response_data.memory_pools_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Copy output voice status
 | 
				
			||||||
 | 
					    size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
 | 
				
			||||||
 | 
					    for (const auto& voice : voices) {
 | 
				
			||||||
 | 
					        std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
 | 
				
			||||||
 | 
					                    sizeof(VoiceOutStatus));
 | 
				
			||||||
 | 
					        voice_out_status_offset += sizeof(VoiceOutStatus);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return output_params;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
 | 
				
			||||||
 | 
					    wave_index = index & 3;
 | 
				
			||||||
 | 
					    is_refresh_pending = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
 | 
				
			||||||
 | 
					    if (!IsPlaying()) {
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (is_refresh_pending) {
 | 
				
			||||||
 | 
					        RefreshBuffer();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const size_t max_size{samples.size() - offset};
 | 
				
			||||||
 | 
					    const size_t dequeue_offset{offset};
 | 
				
			||||||
 | 
					    size_t size{sample_count * STREAM_NUM_CHANNELS};
 | 
				
			||||||
 | 
					    if (size > max_size) {
 | 
				
			||||||
 | 
					        size = max_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
 | 
				
			||||||
 | 
					    offset += size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto& wave_buffer{info.wave_buffer[wave_index]};
 | 
				
			||||||
 | 
					    if (offset == samples.size()) {
 | 
				
			||||||
 | 
					        offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!wave_buffer.is_looping) {
 | 
				
			||||||
 | 
					            SetWaveIndex(wave_index + 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        out_status.wave_buffer_consumed++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (wave_buffer.end_of_stream) {
 | 
				
			||||||
 | 
					            info.play_state = PlayState::Paused;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioRenderer::VoiceState::UpdateState() {
 | 
				
			||||||
 | 
					    if (is_in_use && !info.is_in_use) {
 | 
				
			||||||
 | 
					        // No longer in use, reset state
 | 
				
			||||||
 | 
					        is_refresh_pending = true;
 | 
				
			||||||
 | 
					        wave_index = 0;
 | 
				
			||||||
 | 
					        offset = 0;
 | 
				
			||||||
 | 
					        out_status = {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    is_in_use = info.is_in_use;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioRenderer::VoiceState::RefreshBuffer() {
 | 
				
			||||||
 | 
					    std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
 | 
				
			||||||
 | 
					    Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
 | 
				
			||||||
 | 
					                      info.wave_buffer[wave_index].buffer_sz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
 | 
				
			||||||
 | 
					    case Codec::PcmFormat::Int16: {
 | 
				
			||||||
 | 
					        // PCM16 is played as-is
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case Codec::PcmFormat::Adpcm: {
 | 
				
			||||||
 | 
					        // Decode ADPCM to PCM16
 | 
				
			||||||
 | 
					        Codec::ADPCM_Coeff coeffs;
 | 
				
			||||||
 | 
					        Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
 | 
				
			||||||
 | 
					        new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
 | 
				
			||||||
 | 
					                                         new_samples.size() * sizeof(s16), coeffs, adpcm_state);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
 | 
				
			||||||
 | 
					        UNREACHABLE();
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (info.channel_count) {
 | 
				
			||||||
 | 
					    case 1:
 | 
				
			||||||
 | 
					        // 1 channel is upsampled to 2 channel
 | 
				
			||||||
 | 
					        samples.resize(new_samples.size() * 2);
 | 
				
			||||||
 | 
					        for (size_t index = 0; index < new_samples.size(); ++index) {
 | 
				
			||||||
 | 
					            samples[index * 2] = new_samples[index];
 | 
				
			||||||
 | 
					            samples[index * 2 + 1] = new_samples[index];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 2: {
 | 
				
			||||||
 | 
					        // 2 channel is played as is
 | 
				
			||||||
 | 
					        samples = std::move(new_samples);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
 | 
				
			||||||
 | 
					        UNREACHABLE();
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    is_refresh_pending = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static constexpr s16 ClampToS16(s32 value) {
 | 
				
			||||||
 | 
					    return static_cast<s16>(std::clamp(value, -32768, 32767));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
 | 
				
			||||||
 | 
					    constexpr size_t BUFFER_SIZE{512};
 | 
				
			||||||
 | 
					    std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto& voice : voices) {
 | 
				
			||||||
 | 
					        if (!voice.IsPlaying()) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size_t offset{};
 | 
				
			||||||
 | 
					        s64 samples_remaining{BUFFER_SIZE};
 | 
				
			||||||
 | 
					        while (samples_remaining > 0) {
 | 
				
			||||||
 | 
					            const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (samples.empty()) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            samples_remaining -= samples.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const auto& sample : samples) {
 | 
				
			||||||
 | 
					                const s32 buffer_sample{buffer[offset]};
 | 
				
			||||||
 | 
					                buffer[offset++] =
 | 
				
			||||||
 | 
					                    ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    audio_core->QueueBuffer(stream, tag, std::move(buffer));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioRenderer::ReleaseAndQueueBuffers() {
 | 
				
			||||||
 | 
					    const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
 | 
				
			||||||
 | 
					    for (const auto& tag : released_buffers) {
 | 
				
			||||||
 | 
					        QueueMixedBuffer(tag);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace AudioCore
 | 
				
			||||||
							
								
								
									
										206
									
								
								src/audio_core/audio_renderer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								src/audio_core/audio_renderer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audio_core/audio_out.h"
 | 
				
			||||||
 | 
					#include "audio_core/codec.h"
 | 
				
			||||||
 | 
					#include "audio_core/stream.h"
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/swap.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/event.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace AudioCore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class PlayState : u8 {
 | 
				
			||||||
 | 
					    Started = 0,
 | 
				
			||||||
 | 
					    Stopped = 1,
 | 
				
			||||||
 | 
					    Paused = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AudioRendererParameter {
 | 
				
			||||||
 | 
					    u32_le sample_rate;
 | 
				
			||||||
 | 
					    u32_le sample_count;
 | 
				
			||||||
 | 
					    u32_le unknown_8;
 | 
				
			||||||
 | 
					    u32_le unknown_c;
 | 
				
			||||||
 | 
					    u32_le voice_count;
 | 
				
			||||||
 | 
					    u32_le sink_count;
 | 
				
			||||||
 | 
					    u32_le effect_count;
 | 
				
			||||||
 | 
					    u32_le unknown_1c;
 | 
				
			||||||
 | 
					    u8 unknown_20;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(3);
 | 
				
			||||||
 | 
					    u32_le splitter_count;
 | 
				
			||||||
 | 
					    u32_le unknown_2c;
 | 
				
			||||||
 | 
					    INSERT_PADDING_WORDS(1);
 | 
				
			||||||
 | 
					    u32_le revision;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class MemoryPoolStates : u32 { // Should be LE
 | 
				
			||||||
 | 
					    Invalid = 0x0,
 | 
				
			||||||
 | 
					    Unknown = 0x1,
 | 
				
			||||||
 | 
					    RequestDetach = 0x2,
 | 
				
			||||||
 | 
					    Detached = 0x3,
 | 
				
			||||||
 | 
					    RequestAttach = 0x4,
 | 
				
			||||||
 | 
					    Attached = 0x5,
 | 
				
			||||||
 | 
					    Released = 0x6,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MemoryPoolEntry {
 | 
				
			||||||
 | 
					    MemoryPoolStates state;
 | 
				
			||||||
 | 
					    u32_le unknown_4;
 | 
				
			||||||
 | 
					    u32_le unknown_8;
 | 
				
			||||||
 | 
					    u32_le unknown_c;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MemoryPoolInfo {
 | 
				
			||||||
 | 
					    u64_le pool_address;
 | 
				
			||||||
 | 
					    u64_le pool_size;
 | 
				
			||||||
 | 
					    MemoryPoolStates pool_state;
 | 
				
			||||||
 | 
					    INSERT_PADDING_WORDS(3); // Unknown
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
 | 
				
			||||||
 | 
					struct BiquadFilter {
 | 
				
			||||||
 | 
					    u8 enable;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(1);
 | 
				
			||||||
 | 
					    std::array<s16_le, 3> numerator;
 | 
				
			||||||
 | 
					    std::array<s16_le, 2> denominator;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct WaveBuffer {
 | 
				
			||||||
 | 
					    u64_le buffer_addr;
 | 
				
			||||||
 | 
					    u64_le buffer_sz;
 | 
				
			||||||
 | 
					    s32_le start_sample_offset;
 | 
				
			||||||
 | 
					    s32_le end_sample_offset;
 | 
				
			||||||
 | 
					    u8 is_looping;
 | 
				
			||||||
 | 
					    u8 end_of_stream;
 | 
				
			||||||
 | 
					    u8 sent_to_server;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(5);
 | 
				
			||||||
 | 
					    u64 context_addr;
 | 
				
			||||||
 | 
					    u64 context_sz;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(8);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct VoiceInfo {
 | 
				
			||||||
 | 
					    u32_le id;
 | 
				
			||||||
 | 
					    u32_le node_id;
 | 
				
			||||||
 | 
					    u8 is_new;
 | 
				
			||||||
 | 
					    u8 is_in_use;
 | 
				
			||||||
 | 
					    PlayState play_state;
 | 
				
			||||||
 | 
					    u8 sample_format;
 | 
				
			||||||
 | 
					    u32_le sample_rate;
 | 
				
			||||||
 | 
					    u32_le priority;
 | 
				
			||||||
 | 
					    u32_le sorting_order;
 | 
				
			||||||
 | 
					    u32_le channel_count;
 | 
				
			||||||
 | 
					    float_le pitch;
 | 
				
			||||||
 | 
					    float_le volume;
 | 
				
			||||||
 | 
					    std::array<BiquadFilter, 2> biquad_filter;
 | 
				
			||||||
 | 
					    u32_le wave_buffer_count;
 | 
				
			||||||
 | 
					    u32_le wave_buffer_head;
 | 
				
			||||||
 | 
					    INSERT_PADDING_WORDS(1);
 | 
				
			||||||
 | 
					    u64_le additional_params_addr;
 | 
				
			||||||
 | 
					    u64_le additional_params_sz;
 | 
				
			||||||
 | 
					    u32_le mix_id;
 | 
				
			||||||
 | 
					    u32_le splitter_info_id;
 | 
				
			||||||
 | 
					    std::array<WaveBuffer, 4> wave_buffer;
 | 
				
			||||||
 | 
					    std::array<u32_le, 6> voice_channel_resource_ids;
 | 
				
			||||||
 | 
					    INSERT_PADDING_BYTES(24);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct VoiceOutStatus {
 | 
				
			||||||
 | 
					    u64_le played_sample_count;
 | 
				
			||||||
 | 
					    u32_le wave_buffer_consumed;
 | 
				
			||||||
 | 
					    u32_le voice_drops_count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct UpdateDataHeader {
 | 
				
			||||||
 | 
					    UpdateDataHeader() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    explicit UpdateDataHeader(const AudioRendererParameter& config) {
 | 
				
			||||||
 | 
					        revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
 | 
				
			||||||
 | 
					        behavior_size = 0xb0;
 | 
				
			||||||
 | 
					        memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
 | 
				
			||||||
 | 
					        voices_size = config.voice_count * 0x10;
 | 
				
			||||||
 | 
					        voice_resource_size = 0x0;
 | 
				
			||||||
 | 
					        effects_size = config.effect_count * 0x10;
 | 
				
			||||||
 | 
					        mixes_size = 0x0;
 | 
				
			||||||
 | 
					        sinks_size = config.sink_count * 0x20;
 | 
				
			||||||
 | 
					        performance_manager_size = 0x10;
 | 
				
			||||||
 | 
					        total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
 | 
				
			||||||
 | 
					                     effects_size + sinks_size + performance_manager_size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u32_le revision;
 | 
				
			||||||
 | 
					    u32_le behavior_size;
 | 
				
			||||||
 | 
					    u32_le memory_pools_size;
 | 
				
			||||||
 | 
					    u32_le voices_size;
 | 
				
			||||||
 | 
					    u32_le voice_resource_size;
 | 
				
			||||||
 | 
					    u32_le effects_size;
 | 
				
			||||||
 | 
					    u32_le mixes_size;
 | 
				
			||||||
 | 
					    u32_le sinks_size;
 | 
				
			||||||
 | 
					    u32_le performance_manager_size;
 | 
				
			||||||
 | 
					    INSERT_PADDING_WORDS(6);
 | 
				
			||||||
 | 
					    u32_le total_size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AudioRenderer {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
 | 
				
			||||||
 | 
					    std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
 | 
				
			||||||
 | 
					    void QueueMixedBuffer(Buffer::Tag tag);
 | 
				
			||||||
 | 
					    void ReleaseAndQueueBuffers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    class VoiceState {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        bool IsPlaying() const {
 | 
				
			||||||
 | 
					            return is_in_use && info.play_state == PlayState::Started;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const VoiceOutStatus& GetOutStatus() const {
 | 
				
			||||||
 | 
					            return out_status;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const VoiceInfo& GetInfo() const {
 | 
				
			||||||
 | 
					            return info;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        VoiceInfo& Info() {
 | 
				
			||||||
 | 
					            return info;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void SetWaveIndex(size_t index);
 | 
				
			||||||
 | 
					        std::vector<s16> DequeueSamples(size_t sample_count);
 | 
				
			||||||
 | 
					        void UpdateState();
 | 
				
			||||||
 | 
					        void RefreshBuffer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        bool is_in_use{};
 | 
				
			||||||
 | 
					        bool is_refresh_pending{};
 | 
				
			||||||
 | 
					        size_t wave_index{};
 | 
				
			||||||
 | 
					        size_t offset{};
 | 
				
			||||||
 | 
					        Codec::ADPCMState adpcm_state{};
 | 
				
			||||||
 | 
					        std::vector<s16> samples;
 | 
				
			||||||
 | 
					        VoiceOutStatus out_status{};
 | 
				
			||||||
 | 
					        VoiceInfo info{};
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AudioRendererParameter worker_params;
 | 
				
			||||||
 | 
					    Kernel::SharedPtr<Kernel::Event> buffer_event;
 | 
				
			||||||
 | 
					    std::vector<VoiceState> voices;
 | 
				
			||||||
 | 
					    std::unique_ptr<AudioCore::AudioOut> audio_core;
 | 
				
			||||||
 | 
					    AudioCore::StreamPtr stream;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace AudioCore
 | 
				
			||||||
@ -18,11 +18,16 @@ class Buffer {
 | 
				
			|||||||
public:
 | 
					public:
 | 
				
			||||||
    using Tag = u64;
 | 
					    using Tag = u64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
 | 
					    Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns the raw audio data for the buffer
 | 
					    /// Returns the raw audio data for the buffer
 | 
				
			||||||
    const std::vector<u8>& GetData() const {
 | 
					    std::vector<s16>& Samples() {
 | 
				
			||||||
        return data;
 | 
					        return samples;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the raw audio data for the buffer
 | 
				
			||||||
 | 
					    const std::vector<s16>& GetSamples() const {
 | 
				
			||||||
 | 
					        return samples;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns the buffer tag, this is provided by the game to the audout service
 | 
					    /// Returns the buffer tag, this is provided by the game to the audout service
 | 
				
			||||||
@ -32,7 +37,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    Tag tag;
 | 
					    Tag tag;
 | 
				
			||||||
    std::vector<u8> data;
 | 
					    std::vector<s16> samples;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using BufferPtr = std::shared_ptr<Buffer>;
 | 
					using BufferPtr = std::shared_ptr<Buffer>;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										77
									
								
								src/audio_core/codec.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/audio_core/codec.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audio_core/codec.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace AudioCore::Codec {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
 | 
				
			||||||
 | 
					                             ADPCMState& state) {
 | 
				
			||||||
 | 
					    // GC-ADPCM with scale factor and variable coefficients.
 | 
				
			||||||
 | 
					    // Frames are 8 bytes long containing 14 samples each.
 | 
				
			||||||
 | 
					    // Samples are 4 bits (one nibble) long.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constexpr size_t FRAME_LEN = 8;
 | 
				
			||||||
 | 
					    constexpr size_t SAMPLES_PER_FRAME = 14;
 | 
				
			||||||
 | 
					    constexpr std::array<int, 16> SIGNED_NIBBLES = {
 | 
				
			||||||
 | 
					        {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
 | 
				
			||||||
 | 
					    const size_t ret_size =
 | 
				
			||||||
 | 
					        sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
 | 
				
			||||||
 | 
					    std::vector<s16> ret(ret_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int yn1 = state.yn1, yn2 = state.yn2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const size_t NUM_FRAMES =
 | 
				
			||||||
 | 
					        (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
 | 
				
			||||||
 | 
					    for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
 | 
				
			||||||
 | 
					        const int frame_header = data[framei * FRAME_LEN];
 | 
				
			||||||
 | 
					        const int scale = 1 << (frame_header & 0xF);
 | 
				
			||||||
 | 
					        const int idx = (frame_header >> 4) & 0x7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Coefficients are fixed point with 11 bits fractional part.
 | 
				
			||||||
 | 
					        const int coef1 = coeff[idx * 2 + 0];
 | 
				
			||||||
 | 
					        const int coef2 = coeff[idx * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decodes an audio sample. One nibble produces one sample.
 | 
				
			||||||
 | 
					        const auto decode_sample = [&](const int nibble) -> s16 {
 | 
				
			||||||
 | 
					            const int xn = nibble * scale;
 | 
				
			||||||
 | 
					            // We first transform everything into 11 bit fixed point, perform the second order
 | 
				
			||||||
 | 
					            // digital filter, then transform back.
 | 
				
			||||||
 | 
					            // 0x400 == 0.5 in 11 bit fixed point.
 | 
				
			||||||
 | 
					            // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
 | 
				
			||||||
 | 
					            int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
 | 
				
			||||||
 | 
					            // Clamp to output range.
 | 
				
			||||||
 | 
					            val = std::clamp<s32>(val, -32768, 32767);
 | 
				
			||||||
 | 
					            // Advance output feedback.
 | 
				
			||||||
 | 
					            yn2 = yn1;
 | 
				
			||||||
 | 
					            yn1 = val;
 | 
				
			||||||
 | 
					            return static_cast<s16>(val);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size_t outputi = framei * SAMPLES_PER_FRAME;
 | 
				
			||||||
 | 
					        size_t datai = framei * FRAME_LEN + 1;
 | 
				
			||||||
 | 
					        for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
 | 
				
			||||||
 | 
					            const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
 | 
				
			||||||
 | 
					            ret[outputi] = sample1;
 | 
				
			||||||
 | 
					            outputi++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
 | 
				
			||||||
 | 
					            ret[outputi] = sample2;
 | 
				
			||||||
 | 
					            outputi++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            datai++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state.yn1 = yn1;
 | 
				
			||||||
 | 
					    state.yn2 = yn2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace AudioCore::Codec
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/audio_core/codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/audio_core/codec.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace AudioCore::Codec {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class PcmFormat : u32 {
 | 
				
			||||||
 | 
					    Invalid = 0,
 | 
				
			||||||
 | 
					    Int8 = 1,
 | 
				
			||||||
 | 
					    Int16 = 2,
 | 
				
			||||||
 | 
					    Int24 = 3,
 | 
				
			||||||
 | 
					    Int32 = 4,
 | 
				
			||||||
 | 
					    PcmFloat = 5,
 | 
				
			||||||
 | 
					    Adpcm = 6,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// See: Codec::DecodeADPCM
 | 
				
			||||||
 | 
					struct ADPCMState {
 | 
				
			||||||
 | 
					    // Two historical samples from previous processed buffer,
 | 
				
			||||||
 | 
					    // required for ADPCM decoding
 | 
				
			||||||
 | 
					    s16 yn1; ///< y[n-1]
 | 
				
			||||||
 | 
					    s16 yn2; ///< y[n-2]
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using ADPCM_Coeff = std::array<s16, 16>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param data Pointer to buffer that contains ADPCM data to decode
 | 
				
			||||||
 | 
					 * @param size Size of buffer in bytes
 | 
				
			||||||
 | 
					 * @param coeff ADPCM coefficients
 | 
				
			||||||
 | 
					 * @param state ADPCM state, this is updated with new state
 | 
				
			||||||
 | 
					 * @return Decoded stereo signed PCM16 data, sample_count in length
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
 | 
				
			||||||
 | 
					                             ADPCMState& state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace AudioCore::Codec
 | 
				
			||||||
@ -13,20 +13,30 @@ namespace AudioCore {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SinkStreamImpl final : public SinkStream {
 | 
					class SinkStreamImpl final : public SinkStream {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
 | 
					    SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
 | 
				
			||||||
        cubeb_stream_params params;
 | 
					                   const std::string& name)
 | 
				
			||||||
        params.rate = 48000;
 | 
					        : ctx{ctx}, num_channels{num_channels_} {
 | 
				
			||||||
        params.channels = GetNumChannels();
 | 
					 | 
				
			||||||
        params.format = CUBEB_SAMPLE_S16NE;
 | 
					 | 
				
			||||||
        params.layout = CUBEB_LAYOUT_STEREO;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u32 minimum_latency = 0;
 | 
					        if (num_channels == 6) {
 | 
				
			||||||
 | 
					            // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
 | 
				
			||||||
 | 
					            // channel for now
 | 
				
			||||||
 | 
					            is_6_channel = true;
 | 
				
			||||||
 | 
					            num_channels = 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cubeb_stream_params params{};
 | 
				
			||||||
 | 
					        params.rate = sample_rate;
 | 
				
			||||||
 | 
					        params.channels = num_channels;
 | 
				
			||||||
 | 
					        params.format = CUBEB_SAMPLE_S16NE;
 | 
				
			||||||
 | 
					        params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        u32 minimum_latency{};
 | 
				
			||||||
        if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) {
 | 
					        if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) {
 | 
				
			||||||
            LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
 | 
					            LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
 | 
					        if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
 | 
				
			||||||
                              output_device, ¶ms, std::max(512u, minimum_latency),
 | 
					                              ¶ms, std::max(512u, minimum_latency),
 | 
				
			||||||
                              &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
 | 
					                              &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
 | 
				
			||||||
                              this) != CUBEB_OK) {
 | 
					                              this) != CUBEB_OK) {
 | 
				
			||||||
            LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
 | 
					            LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
 | 
				
			||||||
@ -51,33 +61,29 @@ public:
 | 
				
			|||||||
        cubeb_stream_destroy(stream_backend);
 | 
					        cubeb_stream_destroy(stream_backend);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
 | 
					    void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
 | 
				
			||||||
        if (!ctx) {
 | 
					        if (!ctx) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        queue.reserve(queue.size() + sample_count * GetNumChannels());
 | 
					        queue.reserve(queue.size() + samples.size() * GetNumChannels());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (num_channels == 2) {
 | 
					        if (is_6_channel) {
 | 
				
			||||||
            // Copy as-is
 | 
					 | 
				
			||||||
            std::copy(samples, samples + sample_count * GetNumChannels(),
 | 
					 | 
				
			||||||
                      std::back_inserter(queue));
 | 
					 | 
				
			||||||
        } else if (num_channels == 6) {
 | 
					 | 
				
			||||||
            // Downsample 6 channels to 2
 | 
					            // Downsample 6 channels to 2
 | 
				
			||||||
            const size_t sample_count_copy_size = sample_count * num_channels * 2;
 | 
					            const size_t sample_count_copy_size = samples.size() * 2;
 | 
				
			||||||
            queue.reserve(sample_count_copy_size);
 | 
					            queue.reserve(sample_count_copy_size);
 | 
				
			||||||
            for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
 | 
					            for (size_t i = 0; i < samples.size(); i += num_channels) {
 | 
				
			||||||
                queue.push_back(samples[i]);
 | 
					                queue.push_back(samples[i]);
 | 
				
			||||||
                queue.push_back(samples[i + 1]);
 | 
					                queue.push_back(samples[i + 1]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            ASSERT_MSG(false, "Unimplemented");
 | 
					            // Copy as-is
 | 
				
			||||||
 | 
					            std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 GetNumChannels() const {
 | 
					    u32 GetNumChannels() const {
 | 
				
			||||||
        // Only support 2-channel stereo output for now
 | 
					        return num_channels;
 | 
				
			||||||
        return 2;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
@ -85,6 +91,8 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    cubeb* ctx{};
 | 
					    cubeb* ctx{};
 | 
				
			||||||
    cubeb_stream* stream_backend{};
 | 
					    cubeb_stream* stream_backend{};
 | 
				
			||||||
 | 
					    u32 num_channels{};
 | 
				
			||||||
 | 
					    bool is_6_channel{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<s16> queue;
 | 
					    std::vector<s16> queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
 | 
				
			|||||||
    cubeb_destroy(ctx);
 | 
					    cubeb_destroy(ctx);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
 | 
					SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
 | 
				
			||||||
    sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
 | 
					                                         const std::string& name) {
 | 
				
			||||||
 | 
					    sink_streams.push_back(
 | 
				
			||||||
 | 
					        std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
 | 
				
			||||||
    return *sink_streams.back();
 | 
					    return *sink_streams.back();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,8 @@ public:
 | 
				
			|||||||
    explicit CubebSink(std::string device_id);
 | 
					    explicit CubebSink(std::string device_id);
 | 
				
			||||||
    ~CubebSink() override;
 | 
					    ~CubebSink() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
 | 
					    SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
 | 
				
			||||||
 | 
					                                  const std::string& name) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    cubeb* ctx{};
 | 
					    cubeb* ctx{};
 | 
				
			||||||
 | 
				
			|||||||
@ -13,14 +13,14 @@ public:
 | 
				
			|||||||
    explicit NullSink(std::string){};
 | 
					    explicit NullSink(std::string){};
 | 
				
			||||||
    ~NullSink() override = default;
 | 
					    ~NullSink() override = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
 | 
					    SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
 | 
				
			||||||
 | 
					                                  const std::string& /*name*/) override {
 | 
				
			||||||
        return null_sink_stream;
 | 
					        return null_sink_stream;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    struct NullSinkStreamImpl final : SinkStream {
 | 
					    struct NullSinkStreamImpl final : SinkStream {
 | 
				
			||||||
        void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
 | 
					        void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
 | 
				
			||||||
                            size_t /*sample_count*/) override {}
 | 
					 | 
				
			||||||
    } null_sink_stream;
 | 
					    } null_sink_stream;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "audio_core/sink_stream.h"
 | 
					#include "audio_core/sink_stream.h"
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
 | 
				
			|||||||
class Sink {
 | 
					class Sink {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    virtual ~Sink() = default;
 | 
					    virtual ~Sink() = default;
 | 
				
			||||||
    virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
 | 
					    virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
 | 
				
			||||||
 | 
					                                          const std::string& name) = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using SinkPtr = std::unique_ptr<Sink>;
 | 
					using SinkPtr = std::unique_ptr<Sink>;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,9 +23,8 @@ public:
 | 
				
			|||||||
     * Feed stereo samples to sink.
 | 
					     * Feed stereo samples to sink.
 | 
				
			||||||
     * @param num_channels Number of channels used.
 | 
					     * @param num_channels Number of channels used.
 | 
				
			||||||
     * @param samples Samples in interleaved stereo PCM16 format.
 | 
					     * @param samples Samples in interleaved stereo PCM16 format.
 | 
				
			||||||
     * @param sample_count Number of samples.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
 | 
					    virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
 | 
					using SinkStreamPtr = std::unique_ptr<SinkStream>;
 | 
				
			||||||
 | 
				
			|||||||
@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const {
 | 
				
			|||||||
    return {};
 | 
					    return {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
u32 Stream::GetSampleSize() const {
 | 
					 | 
				
			||||||
    return GetNumChannels() * 2;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
 | 
					Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
 | 
				
			||||||
               SinkStream& sink_stream)
 | 
					               SinkStream& sink_stream, std::string&& name_)
 | 
				
			||||||
    : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
 | 
					    : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
 | 
				
			||||||
      sink_stream{sink_stream} {
 | 
					      sink_stream{sink_stream}, name{std::move(name_)} {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    release_event = CoreTiming::RegisterEvent(
 | 
					    release_event = CoreTiming::RegisterEvent(
 | 
				
			||||||
        "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
 | 
					        name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Stream::Play() {
 | 
					void Stream::Play() {
 | 
				
			||||||
@ -55,17 +51,15 @@ void Stream::Stop() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
 | 
					s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
 | 
				
			||||||
    const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
 | 
					    const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
 | 
				
			||||||
    return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
 | 
					    return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
 | 
					static void VolumeAdjustSamples(std::vector<s16>& samples) {
 | 
				
			||||||
    std::vector<s16> samples(data.size() / sizeof(s16));
 | 
					 | 
				
			||||||
    std::memcpy(samples.data(), data.data(), data.size());
 | 
					 | 
				
			||||||
    const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
 | 
					    const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (volume == 1.0f) {
 | 
					    if (volume == 1.0f) {
 | 
				
			||||||
        return samples;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Implementation of a volume slider with a dynamic range of 60 dB
 | 
					    // Implementation of a volume slider with a dynamic range of 60 dB
 | 
				
			||||||
@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
 | 
				
			|||||||
    for (auto& sample : samples) {
 | 
					    for (auto& sample : samples) {
 | 
				
			||||||
        sample = static_cast<s16>(sample * volume_scale_factor);
 | 
					        sample = static_cast<s16>(sample * volume_scale_factor);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return samples;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Stream::PlayNextBuffer() {
 | 
					void Stream::PlayNextBuffer() {
 | 
				
			||||||
@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
 | 
				
			|||||||
    active_buffer = queued_buffers.front();
 | 
					    active_buffer = queued_buffers.front();
 | 
				
			||||||
    queued_buffers.pop();
 | 
					    queued_buffers.pop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
 | 
					    VolumeAdjustSamples(active_buffer->Samples());
 | 
				
			||||||
    sink_stream.EnqueueSamples(
 | 
					    sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
 | 
				
			||||||
        GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
 | 
					    CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Stream::ReleaseActiveBuffer() {
 | 
					void Stream::ReleaseActiveBuffer() {
 | 
				
			||||||
 | 
					    ASSERT(active_buffer);
 | 
				
			||||||
    released_buffers.push(std::move(active_buffer));
 | 
					    released_buffers.push(std::move(active_buffer));
 | 
				
			||||||
    release_callback();
 | 
					    release_callback();
 | 
				
			||||||
    PlayNextBuffer();
 | 
					    PlayNextBuffer();
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <queue>
 | 
					#include <queue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -33,7 +34,7 @@ public:
 | 
				
			|||||||
    using ReleaseCallback = std::function<void()>;
 | 
					    using ReleaseCallback = std::function<void()>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
 | 
					    Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
 | 
				
			||||||
           SinkStream& sink_stream);
 | 
					           SinkStream& sink_stream, std::string&& name_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Plays the audio stream
 | 
					    /// Plays the audio stream
 | 
				
			||||||
    void Play();
 | 
					    void Play();
 | 
				
			||||||
@ -68,9 +69,6 @@ public:
 | 
				
			|||||||
    /// Gets the number of channels
 | 
					    /// Gets the number of channels
 | 
				
			||||||
    u32 GetNumChannels() const;
 | 
					    u32 GetNumChannels() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Gets the sample size in bytes
 | 
					 | 
				
			||||||
    u32 GetSampleSize() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    /// Current state of the stream
 | 
					    /// Current state of the stream
 | 
				
			||||||
    enum class State {
 | 
					    enum class State {
 | 
				
			||||||
@ -96,6 +94,7 @@ private:
 | 
				
			|||||||
    std::queue<BufferPtr> queued_buffers;   ///< Buffers queued to be played in the stream
 | 
					    std::queue<BufferPtr> queued_buffers;   ///< Buffers queued to be played in the stream
 | 
				
			||||||
    std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
 | 
					    std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
 | 
				
			||||||
    SinkStream& sink_stream;                ///< Output sink for the stream
 | 
					    SinkStream& sink_stream;                ///< Output sink for the stream
 | 
				
			||||||
 | 
					    std::string name;                       ///< Name of the stream, must be unique
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using StreamPtr = std::shared_ptr<Stream>;
 | 
					using StreamPtr = std::shared_ptr<Stream>;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audio_core/codec.h"
 | 
				
			||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
#include "core/core.h"
 | 
					#include "core/core.h"
 | 
				
			||||||
#include "core/hle/ipc_helpers.h"
 | 
					#include "core/hle/ipc_helpers.h"
 | 
				
			||||||
@ -48,7 +50,7 @@ public:
 | 
				
			|||||||
        buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
 | 
					        buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
 | 
					        stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
 | 
				
			||||||
                                       [=]() { buffer_event->Signal(); });
 | 
					                                       "IAudioOut", [=]() { buffer_event->Signal(); });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
@ -111,10 +113,10 @@ private:
 | 
				
			|||||||
        std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
 | 
					        std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
 | 
				
			||||||
        const u64 tag{rp.Pop<u64>()};
 | 
					        const u64 tag{rp.Pop<u64>()};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::vector<u8> data(audio_buffer.buffer_size);
 | 
					        std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
 | 
				
			||||||
        Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
 | 
					        Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
 | 
					        if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
 | 
				
			||||||
            IPC::ResponseBuilder rb{ctx, 2};
 | 
					            IPC::ResponseBuilder rb{ctx, 2};
 | 
				
			||||||
            rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
 | 
					            rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
 | 
				
			|||||||
    rb.Push(RESULT_SUCCESS);
 | 
					    rb.Push(RESULT_SUCCESS);
 | 
				
			||||||
    rb.Push<u32>(DefaultSampleRate);
 | 
					    rb.Push<u32>(DefaultSampleRate);
 | 
				
			||||||
    rb.Push<u32>(params.channel_count);
 | 
					    rb.Push<u32>(params.channel_count);
 | 
				
			||||||
    rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
 | 
					    rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
 | 
				
			||||||
    rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
 | 
					    rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
 | 
				
			||||||
    rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
 | 
					    rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,16 +38,6 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
 | 
					    void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
 | 
				
			||||||
    void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
 | 
					    void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    enum class PcmFormat : u32 {
 | 
					 | 
				
			||||||
        Invalid = 0,
 | 
					 | 
				
			||||||
        Int8 = 1,
 | 
					 | 
				
			||||||
        Int16 = 2,
 | 
					 | 
				
			||||||
        Int24 = 3,
 | 
					 | 
				
			||||||
        Int32 = 4,
 | 
					 | 
				
			||||||
        PcmFloat = 5,
 | 
					 | 
				
			||||||
        Adpcm = 6,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Service::Audio
 | 
					} // namespace Service::Audio
 | 
				
			||||||
 | 
				
			|||||||
@ -15,13 +15,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Service::Audio {
 | 
					namespace Service::Audio {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// TODO(bunnei): Find a proper value for the audio_ticks
 | 
					 | 
				
			||||||
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
 | 
					class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit IAudioRenderer(AudioRendererParameter audren_params)
 | 
					    explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
 | 
				
			||||||
        : ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
 | 
					        : ServiceFramework("IAudioRenderer") {
 | 
				
			||||||
        static const FunctionInfo functions[] = {
 | 
					        static const FunctionInfo functions[] = {
 | 
				
			||||||
            {0, nullptr, "GetAudioRendererSampleRate"},
 | 
					            {0, nullptr, "GetAudioRendererSampleRate"},
 | 
				
			||||||
            {1, nullptr, "GetAudioRendererSampleCount"},
 | 
					            {1, nullptr, "GetAudioRendererSampleCount"},
 | 
				
			||||||
@ -39,21 +36,8 @@ public:
 | 
				
			|||||||
        RegisterHandlers(functions);
 | 
					        RegisterHandlers(functions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        system_event =
 | 
					        system_event =
 | 
				
			||||||
            Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
 | 
					            Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
 | 
				
			||||||
 | 
					        renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
 | 
				
			||||||
        // Register event callback to update the Audio Buffer
 | 
					 | 
				
			||||||
        audio_event = CoreTiming::RegisterEvent(
 | 
					 | 
				
			||||||
            "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
 | 
					 | 
				
			||||||
                UpdateAudioCallback();
 | 
					 | 
				
			||||||
                CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Start the audio event
 | 
					 | 
				
			||||||
        CoreTiming::ScheduleEvent(audio_ticks, audio_event);
 | 
					 | 
				
			||||||
        voice_status_list.resize(worker_params.voice_count);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ~IAudioRenderer() {
 | 
					 | 
				
			||||||
        CoreTiming::UnscheduleEvent(audio_event, 0);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
@ -62,60 +46,9 @@ private:
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
					    void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
				
			||||||
        UpdateDataHeader config{};
 | 
					        ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
 | 
				
			||||||
        auto buf = ctx.ReadBuffer();
 | 
					 | 
				
			||||||
        std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
 | 
					 | 
				
			||||||
        u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
 | 
					 | 
				
			||||||
        std::memcpy(mem_pool_info.data(),
 | 
					 | 
				
			||||||
                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
 | 
					 | 
				
			||||||
                    memory_pool_count * sizeof(MemoryPoolInfo));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::vector<VoiceInfo> voice_info(worker_params.voice_count);
 | 
					 | 
				
			||||||
        std::memcpy(voice_info.data(),
 | 
					 | 
				
			||||||
                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
 | 
					 | 
				
			||||||
                        config.memory_pools_size + config.voice_resource_size,
 | 
					 | 
				
			||||||
                    worker_params.voice_count * sizeof(VoiceInfo));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        UpdateDataHeader response_data{worker_params};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::vector<u8> output(response_data.total_size);
 | 
					 | 
				
			||||||
        std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
 | 
					 | 
				
			||||||
        std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
 | 
					 | 
				
			||||||
        for (unsigned i = 0; i < memory_pool.size(); i++) {
 | 
					 | 
				
			||||||
            if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
 | 
					 | 
				
			||||||
                memory_pool[i].state = MemoryPoolStates::Attached;
 | 
					 | 
				
			||||||
            else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
 | 
					 | 
				
			||||||
                memory_pool[i].state = MemoryPoolStates::Detached;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
 | 
					 | 
				
			||||||
                    response_data.memory_pools_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (unsigned i = 0; i < voice_info.size(); i++) {
 | 
					 | 
				
			||||||
            if (voice_info[i].is_new) {
 | 
					 | 
				
			||||||
                voice_status_list[i].played_sample_count = 0;
 | 
					 | 
				
			||||||
                voice_status_list[i].wave_buffer_consumed = 0;
 | 
					 | 
				
			||||||
            } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
 | 
					 | 
				
			||||||
                for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
 | 
					 | 
				
			||||||
                    voice_status_list[i].played_sample_count +=
 | 
					 | 
				
			||||||
                        (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
 | 
					 | 
				
			||||||
                         voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
 | 
					 | 
				
			||||||
                        2;
 | 
					 | 
				
			||||||
                    voice_status_list[i].wave_buffer_consumed++;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
 | 
					 | 
				
			||||||
                    voice_status_list.data(), response_data.voices_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ctx.WriteBuffer(output);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
					        IPC::ResponseBuilder rb{ctx, 2};
 | 
				
			||||||
        rb.Push(RESULT_SUCCESS);
 | 
					        rb.Push(RESULT_SUCCESS);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
					        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -136,8 +69,6 @@ private:
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
 | 
					    void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
 | 
				
			||||||
        // system_event->Signal();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        IPC::ResponseBuilder rb{ctx, 2, 1};
 | 
					        IPC::ResponseBuilder rb{ctx, 2, 1};
 | 
				
			||||||
        rb.Push(RESULT_SUCCESS);
 | 
					        rb.Push(RESULT_SUCCESS);
 | 
				
			||||||
        rb.PushCopyObjects(system_event);
 | 
					        rb.PushCopyObjects(system_event);
 | 
				
			||||||
@ -145,131 +76,8 @@ private:
 | 
				
			|||||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
					        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    enum class MemoryPoolStates : u32 { // Should be LE
 | 
					 | 
				
			||||||
        Invalid = 0x0,
 | 
					 | 
				
			||||||
        Unknown = 0x1,
 | 
					 | 
				
			||||||
        RequestDetach = 0x2,
 | 
					 | 
				
			||||||
        Detached = 0x3,
 | 
					 | 
				
			||||||
        RequestAttach = 0x4,
 | 
					 | 
				
			||||||
        Attached = 0x5,
 | 
					 | 
				
			||||||
        Released = 0x6,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    enum class PlayStates : u8 {
 | 
					 | 
				
			||||||
        Started = 0,
 | 
					 | 
				
			||||||
        Stopped = 1,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct MemoryPoolEntry {
 | 
					 | 
				
			||||||
        MemoryPoolStates state;
 | 
					 | 
				
			||||||
        u32_le unknown_4;
 | 
					 | 
				
			||||||
        u32_le unknown_8;
 | 
					 | 
				
			||||||
        u32_le unknown_c;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct MemoryPoolInfo {
 | 
					 | 
				
			||||||
        u64_le pool_address;
 | 
					 | 
				
			||||||
        u64_le pool_size;
 | 
					 | 
				
			||||||
        MemoryPoolStates pool_state;
 | 
					 | 
				
			||||||
        INSERT_PADDING_WORDS(3); // Unknown
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct UpdateDataHeader {
 | 
					 | 
				
			||||||
        UpdateDataHeader() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        explicit UpdateDataHeader(const AudioRendererParameter& config) {
 | 
					 | 
				
			||||||
            revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
 | 
					 | 
				
			||||||
            behavior_size = 0xb0;
 | 
					 | 
				
			||||||
            memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
 | 
					 | 
				
			||||||
            voices_size = config.voice_count * 0x10;
 | 
					 | 
				
			||||||
            voice_resource_size = 0x0;
 | 
					 | 
				
			||||||
            effects_size = config.effect_count * 0x10;
 | 
					 | 
				
			||||||
            mixes_size = 0x0;
 | 
					 | 
				
			||||||
            sinks_size = config.sink_count * 0x20;
 | 
					 | 
				
			||||||
            performance_manager_size = 0x10;
 | 
					 | 
				
			||||||
            total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
 | 
					 | 
				
			||||||
                         voices_size + effects_size + sinks_size + performance_manager_size;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        u32_le revision;
 | 
					 | 
				
			||||||
        u32_le behavior_size;
 | 
					 | 
				
			||||||
        u32_le memory_pools_size;
 | 
					 | 
				
			||||||
        u32_le voices_size;
 | 
					 | 
				
			||||||
        u32_le voice_resource_size;
 | 
					 | 
				
			||||||
        u32_le effects_size;
 | 
					 | 
				
			||||||
        u32_le mixes_size;
 | 
					 | 
				
			||||||
        u32_le sinks_size;
 | 
					 | 
				
			||||||
        u32_le performance_manager_size;
 | 
					 | 
				
			||||||
        INSERT_PADDING_WORDS(6);
 | 
					 | 
				
			||||||
        u32_le total_size;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct BiquadFilter {
 | 
					 | 
				
			||||||
        u8 enable;
 | 
					 | 
				
			||||||
        INSERT_PADDING_BYTES(1);
 | 
					 | 
				
			||||||
        s16_le numerator[3];
 | 
					 | 
				
			||||||
        s16_le denominator[2];
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct WaveBuffer {
 | 
					 | 
				
			||||||
        u64_le buffer_addr;
 | 
					 | 
				
			||||||
        u64_le buffer_sz;
 | 
					 | 
				
			||||||
        s32_le start_sample_offset;
 | 
					 | 
				
			||||||
        s32_le end_sample_offset;
 | 
					 | 
				
			||||||
        u8 loop;
 | 
					 | 
				
			||||||
        u8 end_of_stream;
 | 
					 | 
				
			||||||
        u8 sent_to_server;
 | 
					 | 
				
			||||||
        INSERT_PADDING_BYTES(5);
 | 
					 | 
				
			||||||
        u64 context_addr;
 | 
					 | 
				
			||||||
        u64 context_sz;
 | 
					 | 
				
			||||||
        INSERT_PADDING_BYTES(8);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct VoiceInfo {
 | 
					 | 
				
			||||||
        u32_le id;
 | 
					 | 
				
			||||||
        u32_le node_id;
 | 
					 | 
				
			||||||
        u8 is_new;
 | 
					 | 
				
			||||||
        u8 is_in_use;
 | 
					 | 
				
			||||||
        u8 play_state;
 | 
					 | 
				
			||||||
        u8 sample_format;
 | 
					 | 
				
			||||||
        u32_le sample_rate;
 | 
					 | 
				
			||||||
        u32_le priority;
 | 
					 | 
				
			||||||
        u32_le sorting_order;
 | 
					 | 
				
			||||||
        u32_le channel_count;
 | 
					 | 
				
			||||||
        float_le pitch;
 | 
					 | 
				
			||||||
        float_le volume;
 | 
					 | 
				
			||||||
        BiquadFilter biquad_filter[2];
 | 
					 | 
				
			||||||
        u32_le wave_buffer_count;
 | 
					 | 
				
			||||||
        u16_le wave_buffer_head;
 | 
					 | 
				
			||||||
        INSERT_PADDING_BYTES(6);
 | 
					 | 
				
			||||||
        u64_le additional_params_addr;
 | 
					 | 
				
			||||||
        u64_le additional_params_sz;
 | 
					 | 
				
			||||||
        u32_le mix_id;
 | 
					 | 
				
			||||||
        u32_le splitter_info_id;
 | 
					 | 
				
			||||||
        WaveBuffer wave_buffer[4];
 | 
					 | 
				
			||||||
        u32_le voice_channel_resource_ids[6];
 | 
					 | 
				
			||||||
        INSERT_PADDING_BYTES(24);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct VoiceOutStatus {
 | 
					 | 
				
			||||||
        u64_le played_sample_count;
 | 
					 | 
				
			||||||
        u32_le wave_buffer_consumed;
 | 
					 | 
				
			||||||
        INSERT_PADDING_WORDS(1);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// This is used to trigger the audio event callback.
 | 
					 | 
				
			||||||
    CoreTiming::EventType* audio_event;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Kernel::SharedPtr<Kernel::Event> system_event;
 | 
					    Kernel::SharedPtr<Kernel::Event> system_event;
 | 
				
			||||||
    AudioRendererParameter worker_params;
 | 
					    std::unique_ptr<AudioCore::AudioRenderer> renderer;
 | 
				
			||||||
    std::vector<VoiceOutStatus> voice_status_list;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
 | 
					class IAudioDevice final : public ServiceFramework<IAudioDevice> {
 | 
				
			||||||
@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
					void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
				
			||||||
    IPC::RequestParser rp{ctx};
 | 
					    IPC::RequestParser rp{ctx};
 | 
				
			||||||
    auto params = rp.PopRaw<AudioRendererParameter>();
 | 
					    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
 | 
				
			||||||
    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
 | 
					    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rb.Push(RESULT_SUCCESS);
 | 
					    rb.Push(RESULT_SUCCESS);
 | 
				
			||||||
@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
 | 
					void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
 | 
				
			||||||
    IPC::RequestParser rp{ctx};
 | 
					    IPC::RequestParser rp{ctx};
 | 
				
			||||||
    auto params = rp.PopRaw<AudioRendererParameter>();
 | 
					    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
 | 
					    u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
 | 
				
			||||||
    buffer_sz += params.unknown_c * 1024;
 | 
					    buffer_sz += params.unknown_c * 1024;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audio_core/audio_renderer.h"
 | 
				
			||||||
#include "core/hle/service/service.h"
 | 
					#include "core/hle/service/service.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kernel {
 | 
					namespace Kernel {
 | 
				
			||||||
@ -12,24 +13,6 @@ class HLERequestContext;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Service::Audio {
 | 
					namespace Service::Audio {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct AudioRendererParameter {
 | 
					 | 
				
			||||||
    u32_le sample_rate;
 | 
					 | 
				
			||||||
    u32_le sample_count;
 | 
					 | 
				
			||||||
    u32_le unknown_8;
 | 
					 | 
				
			||||||
    u32_le unknown_c;
 | 
					 | 
				
			||||||
    u32_le voice_count;
 | 
					 | 
				
			||||||
    u32_le sink_count;
 | 
					 | 
				
			||||||
    u32_le effect_count;
 | 
					 | 
				
			||||||
    u32_le unknown_1c;
 | 
					 | 
				
			||||||
    u8 unknown_20;
 | 
					 | 
				
			||||||
    INSERT_PADDING_BYTES(3);
 | 
					 | 
				
			||||||
    u32_le splitter_count;
 | 
					 | 
				
			||||||
    u32_le unknown_2c;
 | 
					 | 
				
			||||||
    INSERT_PADDING_WORDS(1);
 | 
					 | 
				
			||||||
    u32_le revision;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AudRenU final : public ServiceFramework<AudRenU> {
 | 
					class AudRenU final : public ServiceFramework<AudRenU> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit AudRenU();
 | 
					    explicit AudRenU();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user