2020-04-19 23:04:05 +02:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
2020-03-27 20:36:02 +01:00
|
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
|
2020-01-24 23:04:16 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <audio/resampler.h>
|
2020-07-07 16:35:34 +02:00
|
|
|
#include <audio/adpcm_decoder.h>
|
2020-02-17 20:11:59 +01:00
|
|
|
#include <audio.h>
|
2020-01-24 23:04:16 +01:00
|
|
|
|
2020-02-17 20:11:59 +01:00
|
|
|
namespace skyline::service::audio::IAudioRenderer {
|
2020-01-24 23:04:16 +01:00
|
|
|
struct BiquadFilter {
|
2020-08-21 15:28:47 +02:00
|
|
|
u8 enable;
|
2020-01-24 23:04:16 +01:00
|
|
|
u8 _pad0_;
|
2020-08-21 15:28:47 +02:00
|
|
|
u16 b0;
|
|
|
|
u16 b1;
|
|
|
|
u16 b2;
|
|
|
|
u16 a1;
|
|
|
|
u16 a2;
|
2020-01-24 23:04:16 +01:00
|
|
|
};
|
2020-08-12 21:12:34 +02:00
|
|
|
static_assert(sizeof(BiquadFilter) == 0xC);
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
struct WaveBuffer {
|
2020-11-08 20:54:15 +01:00
|
|
|
u8 *pointer;
|
2020-08-21 15:28:47 +02:00
|
|
|
u64 size;
|
|
|
|
u32 firstSampleOffset;
|
|
|
|
u32 lastSampleOffset;
|
2020-01-24 23:04:16 +01:00
|
|
|
u8 looping; //!< Whether to loop the buffer
|
|
|
|
u8 lastBuffer; //!< Whether this is the last populated buffer
|
|
|
|
u16 _unk0_;
|
|
|
|
u32 _unk1_;
|
2020-08-21 15:28:47 +02:00
|
|
|
u64 adpcmLoopContextPosition;
|
|
|
|
u64 adpcmLoopContextSize;
|
2020-01-24 23:04:16 +01:00
|
|
|
u64 _unk2_;
|
|
|
|
};
|
|
|
|
static_assert(sizeof(WaveBuffer) == 0x38);
|
|
|
|
|
|
|
|
struct VoiceIn {
|
2020-08-21 15:28:47 +02:00
|
|
|
u32 slot;
|
|
|
|
u32 nodeId;
|
|
|
|
u8 firstUpdate; //!< Whether this voice is newly added
|
2020-01-24 23:04:16 +01:00
|
|
|
u8 acquired; //!< Whether the sample is in use
|
2020-08-21 15:28:47 +02:00
|
|
|
skyline::audio::AudioOutState playbackState;
|
|
|
|
skyline::audio::AudioFormat format;
|
|
|
|
u32 sampleRate;
|
|
|
|
u32 priority;
|
2020-01-24 23:04:16 +01:00
|
|
|
u32 _unk0_;
|
2020-08-21 15:28:47 +02:00
|
|
|
u32 channelCount;
|
|
|
|
float pitch;
|
|
|
|
float volume;
|
|
|
|
std::array<BiquadFilter, 2> biquadFilters;
|
|
|
|
u32 appendedWaveBuffersCount;
|
|
|
|
u32 baseWaveBufferIndex;
|
2020-01-24 23:04:16 +01:00
|
|
|
u32 _unk1_;
|
2020-11-08 20:54:15 +01:00
|
|
|
u32 *adpcmCoeffs;
|
2020-08-21 15:28:47 +02:00
|
|
|
u64 adpcmCoeffsSize;
|
|
|
|
u32 destination;
|
2020-01-24 23:04:16 +01:00
|
|
|
u32 _pad0_;
|
2020-08-21 15:28:47 +02:00
|
|
|
std::array<WaveBuffer, 4> waveBuffers;
|
|
|
|
std::array<u32, 6> voiceChannelResourceIds;
|
2020-01-24 23:04:16 +01:00
|
|
|
u32 _pad1_[6];
|
|
|
|
};
|
|
|
|
static_assert(sizeof(VoiceIn) == 0x170);
|
|
|
|
|
|
|
|
|
|
|
|
struct VoiceOut {
|
2020-08-21 15:28:47 +02:00
|
|
|
u64 playedSamplesCount;
|
|
|
|
u32 playedWaveBuffersCount;
|
2020-01-24 23:04:16 +01:00
|
|
|
u32 voiceDropsCount; //!< The amount of time audio frames have been dropped due to the rendering time limit
|
|
|
|
};
|
|
|
|
static_assert(sizeof(VoiceOut) == 0x10);
|
|
|
|
|
|
|
|
/**
|
2020-09-28 12:05:17 +02:00
|
|
|
* @brief The Voice class manages an audio voice
|
|
|
|
*/
|
2020-01-24 23:04:16 +01:00
|
|
|
class Voice {
|
|
|
|
private:
|
2020-08-21 15:28:47 +02:00
|
|
|
const DeviceState &state;
|
|
|
|
std::array<WaveBuffer, 4> waveBuffers;
|
2020-04-17 23:23:38 +02:00
|
|
|
std::vector<i16> samples; //!< A vector containing processed sample data
|
2020-08-21 15:28:47 +02:00
|
|
|
skyline::audio::Resampler resampler; //!< The resampler object used for changing the sample rate of a wave buffer's stream
|
|
|
|
std::optional<skyline::audio::AdpcmDecoder> adpcmDecoder;
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
bool acquired{false}; //!< If the voice is in use
|
2020-08-21 15:28:47 +02:00
|
|
|
bool bufferReload{true};
|
2020-04-17 23:23:38 +02:00
|
|
|
u8 bufferIndex{}; //!< The index of the wave buffer currently in use
|
|
|
|
u32 sampleOffset{}; //!< The offset in the sample data of the current wave buffer
|
2020-08-21 15:28:47 +02:00
|
|
|
u32 sampleRate{};
|
|
|
|
u8 channelCount{};
|
|
|
|
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped};
|
|
|
|
skyline::audio::AudioFormat format{skyline::audio::AudioFormat::Invalid};
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
/**
|
2020-09-28 12:05:17 +02:00
|
|
|
* @brief Updates the sample buffer with data from the current wave buffer and processes it
|
2020-01-24 23:04:16 +01:00
|
|
|
*/
|
|
|
|
void UpdateBuffers();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sets the current wave buffer index to use
|
|
|
|
* @param index The index to use
|
|
|
|
*/
|
2020-04-17 23:23:38 +02:00
|
|
|
void SetWaveBufferIndex(u8 index);
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
public:
|
2020-08-21 15:28:47 +02:00
|
|
|
VoiceOut output{};
|
|
|
|
float volume{};
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
Voice(const DeviceState &state);
|
|
|
|
|
|
|
|
/**
|
2020-09-28 12:05:17 +02:00
|
|
|
* @brief Reads the input voice data from the guest and sets internal data based off it
|
2020-01-24 23:04:16 +01:00
|
|
|
* @param input The input data struct from guest
|
|
|
|
*/
|
|
|
|
void ProcessInput(const VoiceIn &input);
|
|
|
|
|
|
|
|
/**
|
2020-09-28 12:05:17 +02:00
|
|
|
* @brief Obtains the voices audio sample data, updating it if required
|
2020-01-24 23:04:16 +01:00
|
|
|
* @param maxSamples The maximum amount of samples the output buffer should contain
|
|
|
|
* @return A vector of I16 PCM sample data
|
|
|
|
*/
|
2020-04-17 23:23:38 +02:00
|
|
|
std::vector<i16> &GetBufferData(u32 maxSamples, u32 &outOffset, u32 &outSize);
|
2020-01-24 23:04:16 +01:00
|
|
|
|
|
|
|
/**
|
2020-04-17 23:23:38 +02:00
|
|
|
* @return If the voice is currently playable
|
2020-01-24 23:04:16 +01:00
|
|
|
*/
|
2021-02-06 13:36:58 +01:00
|
|
|
bool Playable() {
|
2020-02-17 20:11:59 +01:00
|
|
|
return acquired && playbackState == skyline::audio::AudioOutState::Started && waveBuffers[bufferIndex].size != 0;
|
2020-01-24 23:04:16 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|