diff options
Diffstat (limited to 'src/core/hle/service')
| -rw-r--r-- | src/core/hle/service/audio/audout_u.cpp | 195 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audout_u.h | 12 | 
2 files changed, 114 insertions, 93 deletions
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 1dcd84d98..a15d53ff8 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -5,8 +5,7 @@  #include <array>  #include <vector>  #include "common/logging/log.h" -#include "core/core_timing.h" -#include "core/core_timing_util.h" +#include "core/core.h"  #include "core/hle/ipc_helpers.h"  #include "core/hle/kernel/event.h"  #include "core/hle/kernel/hle_ipc.h" @@ -14,17 +13,22 @@  namespace Service::Audio { -/// Switch sample rate frequency -constexpr u32 sample_rate{48000}; -/// TODO(st4rk): dynamic number of channels, as I think Switch has support -/// to more audio channels (probably when Docked I guess) -constexpr u32 audio_channels{2}; -/// TODO(st4rk): find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; +namespace ErrCodes { +enum { +    ErrorUnknown = 2, +    BufferCountExceeded = 8, +}; +} + +constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; +constexpr int DefaultSampleRate{48000};  class IAudioOut final : public ServiceFramework<IAudioOut> {  public: -    IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { +    IAudioOut(AudoutParams audio_params) +        : ServiceFramework("IAudioOut"), audio_params(audio_params), +          audio_core(Core::System::GetInstance().AudioCore()) { +          static const FunctionInfo functions[] = {              {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},              {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, @@ -32,66 +36,65 @@ public:              {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},              {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},              {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, -            {6, nullptr, "ContainsAudioOutBuffer"}, +            {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},              {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},              {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, -            {9, nullptr, "GetAudioOutBufferCount"}, +            {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},              {10, nullptr, "GetAudioOutPlayedSampleCount"},              {11, nullptr, "FlushAudioOutBuffers"},          };          RegisterHandlers(functions);          // This is the event handle used to check if the audio buffer was released -        buffer_event = -            Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); - -        // Register event callback to update the Audio Buffer -        audio_event = CoreTiming::RegisterEvent( -            "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) { -                UpdateAudioBuffersCallback(); -                CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); -            }); - -        // Start the audio event -        CoreTiming::ScheduleEvent(audio_ticks, audio_event); -    } +        buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); -    ~IAudioOut() { -        CoreTiming::UnscheduleEvent(audio_event, 0); +        stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, +                                       [=]() { buffer_event->Signal(); });      }  private: +    struct AudioBuffer { +        u64_le next; +        u64_le buffer; +        u64_le buffer_capacity; +        u64_le buffer_size; +        u64_le offset; +    }; +    static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); +      void GetAudioOutState(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_Audio, "called");          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(RESULT_SUCCESS); -        rb.Push(static_cast<u32>(audio_out_state)); +        rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));      }      void StartAudioOut(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); +        LOG_DEBUG(Service_Audio, "called"); + +        if (stream->IsPlaying()) { +            IPC::ResponseBuilder rb{ctx, 2}; +            rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); +            return; +        } -        // Start audio -        audio_out_state = AudioState::Started; +        audio_core.StartStream(stream);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS);      }      void StopAudioOut(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); - -        // Stop audio -        audio_out_state = AudioState::Stopped; +        LOG_DEBUG(Service_Audio, "called"); -        queue_keys.clear(); +        audio_core.StopStream(stream);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS);      }      void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); +        LOG_DEBUG(Service_Audio, "called");          IPC::ResponseBuilder rb{ctx, 2, 1};          rb.Push(RESULT_SUCCESS); @@ -99,101 +102,107 @@ private:      }      void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); +        LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());          IPC::RequestParser rp{ctx}; -        const u64 key{rp.Pop<u64>()}; -        queue_keys.insert(queue_keys.begin(), key); +        const auto& input_buffer{ctx.ReadBuffer()}; +        ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), +                   "AudioBuffer input is an invalid size!"); +        AudioBuffer audio_buffer{}; +        std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); +        const u64 tag{rp.Pop<u64>()}; + +        std::vector<u8> data(audio_buffer.buffer_size); +        Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size()); + +        if (!audio_core.QueueBuffer(stream, tag, std::move(data))) { +            IPC::ResponseBuilder rb{ctx, 2}; +            rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); +        }          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS);      }      void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); - -        // TODO(st4rk): This is how libtransistor currently implements the -        // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address -        // is used to know which buffer should be filled with data and send again to the service -        // through AppendAudioOutBuffer. Check if this is the proper way to do it. -        u64 key{0}; - -        if (queue_keys.size()) { -            key = queue_keys.back(); -            queue_keys.pop_back(); -        } +        LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); +        IPC::RequestParser rp{ctx}; +        const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; +        const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; -        ctx.WriteBuffer(&key, sizeof(u64)); +        std::vector<u64> tags{released_buffers}; +        tags.resize(max_count); +        ctx.WriteBuffer(tags);          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(RESULT_SUCCESS); -        // TODO(st4rk): This might be the total of released buffers, needs to be verified on -        // hardware -        rb.Push<u32>(static_cast<u32>(queue_keys.size())); +        rb.Push<u32>(static_cast<u32>(released_buffers.size()));      } -    void UpdateAudioBuffersCallback() { -        if (audio_out_state != AudioState::Started) { -            return; -        } - -        if (queue_keys.empty()) { -            return; -        } +    void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_Audio, "called"); +        IPC::RequestParser rp{ctx}; +        const u64 tag{rp.Pop<u64>()}; +        IPC::ResponseBuilder rb{ctx, 3}; +        rb.Push(RESULT_SUCCESS); +        rb.Push(stream->ContainsBuffer(tag)); +    } -        buffer_event->Signal(); +    void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_Audio, "called"); +        IPC::ResponseBuilder rb{ctx, 3}; +        rb.Push(RESULT_SUCCESS); +        rb.Push(static_cast<u32>(stream->GetQueueSize()));      } -    enum class AudioState : u32 { -        Started, -        Stopped, -    }; +    AudioCore::AudioOut& audio_core; +    AudioCore::StreamPtr stream; -    /// This is used to trigger the audio event callback that is going to read the samples from the -    /// audio_buffer list and enqueue the samples using the sink (audio_core). -    CoreTiming::EventType* audio_event; +    AudoutParams audio_params{};      /// This is the evend handle used to check if the audio buffer was released      Kernel::SharedPtr<Kernel::Event> buffer_event; - -    /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor -    /// uses the key as an address in the App, so we need to return when the -    /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because -    /// libtransistor uses the key returned as an pointer. -    std::vector<u64> queue_keys; - -    AudioState audio_out_state;  };  void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { -    LOG_WARNING(Service_Audio, "(STUBBED) called"); +    LOG_DEBUG(Service_Audio, "called");      IPC::RequestParser rp{ctx}; -    constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; -    ctx.WriteBuffer(audio_interface); +    ctx.WriteBuffer(DefaultDevice);      IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);      rb.Push(RESULT_SUCCESS); -    // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, -    // it's highly possible to have more than one interface (despite that libtransistor requires -    // only one). -    rb.Push<u32>(1); +    rb.Push<u32>(1); // Amount of audio devices  }  void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { -    LOG_WARNING(Service_Audio, "(STUBBED) called"); +    LOG_DEBUG(Service_Audio, "called"); -    if (!audio_out_interface) { -        audio_out_interface = std::make_shared<IAudioOut>(); +    ctx.WriteBuffer(DefaultDevice); +    IPC::RequestParser rp{ctx}; +    auto params{rp.PopRaw<AudoutParams>()}; +    if (params.channel_count <= 2) { +        // Mono does not exist for audout +        params.channel_count = 2; +    } else { +        params.channel_count = 6;      } +    if (!params.sample_rate) { +        params.sample_rate = DefaultSampleRate; +    } + +    // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl +    // will likely need to be updated as well. +    ASSERT_MSG(!audio_out_interface, "Unimplemented"); +    audio_out_interface = std::make_shared<IAudioOut>(std::move(params));      IPC::ResponseBuilder rb{ctx, 6, 0, 1};      rb.Push(RESULT_SUCCESS); -    rb.Push<u32>(sample_rate); -    rb.Push<u32>(audio_channels); +    rb.Push<u32>(DefaultSampleRate); +    rb.Push<u32>(params.channel_count);      rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); -    rb.Push<u32>(0); // This field is unknown +    rb.Push<u32>(static_cast<u32>(AudioState::Stopped));      rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);  } diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 847d86aa6..bc43f1f44 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -12,6 +12,18 @@ class HLERequestContext;  namespace Service::Audio { +struct AudoutParams { +    s32_le sample_rate; +    u16_le channel_count; +    INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); + +enum class AudioState : u32 { +    Started, +    Stopped, +}; +  class IAudioOut;  class AudOutU final : public ServiceFramework<AudOutU> {  | 
