diff --git a/src/common/audio_stream.cpp b/src/common/audio_stream.cpp index 0c0e816a3..e415ddacb 100644 --- a/src/common/audio_stream.cpp +++ b/src/common/audio_stream.cpp @@ -32,6 +32,12 @@ bool AudioStream::Reconfigure(u32 output_sample_rate /*= DefaultOutputSampleRate return true; } +void AudioStream::SetOutputVolume(s32 volume) +{ + std::unique_lock lock(m_buffer_mutex); + m_output_volume = volume; +} + void AudioStream::PauseOutput(bool paused) { if (m_output_paused == paused) @@ -123,6 +129,16 @@ void AudioStream::EndWrite(u32 num_frames) m_buffer_mutex.unlock(); } +float AudioStream::GetMinLatency(u32 sample_rate, u32 buffer_size, u32 buffer_count) +{ + return (static_cast(buffer_size) / static_cast(sample_rate)); +} + +float AudioStream::GetMaxLatency(u32 sample_rate, u32 buffer_size, u32 buffer_count) +{ + return (static_cast(buffer_size * (buffer_count - 1)) / static_cast(sample_rate)); +} + u32 AudioStream::GetSamplesAvailable() const { // TODO: Use atomic loads @@ -146,8 +162,9 @@ u32 AudioStream::ReadSamples(SampleType* samples, u32 num_samples) const u32 from_this_buffer = std::min(m_buffer_size - buffer.read_position, remaining_samples); const u32 copy_count = from_this_buffer * m_channels; - std::memcpy(samples, &buffer.data[buffer.read_position * m_channels], copy_count * sizeof(SampleType)); - samples += copy_count; + const SampleType* read_pointer = &buffer.data[buffer.read_position * m_channels]; + for (u32 i = 0; i < copy_count; i++) + *(samples++) = ApplyVolume(*(read_pointer++), m_output_volume); remaining_samples -= from_this_buffer; buffer.read_position += from_this_buffer; diff --git a/src/common/audio_stream.h b/src/common/audio_stream.h index 030bc28d2..0e00c2a3b 100644 --- a/src/common/audio_stream.h +++ b/src/common/audio_stream.h @@ -26,11 +26,13 @@ public: u32 GetChannels() const { return m_channels; } u32 GetBufferSize() const { return m_buffer_size; } u32 GetBufferCount() const { return static_cast(m_buffers.size()); } + s32 GetOutputVolume() const { return m_output_volume; } bool IsSyncing() const { return m_sync; } bool Reconfigure(u32 output_sample_rate = DefaultOutputSampleRate, u32 channels = 1, u32 buffer_size = DefaultBufferSize, u32 buffer_count = DefaultBufferCount); void SetSync(bool enable) { m_sync = enable; } + void SetOutputVolume(s32 volume); void PauseOutput(bool paused); void EmptyBuffers(); @@ -45,12 +47,21 @@ public: static std::unique_ptr CreateCubebAudioStream(); + // Latency computation - returns values in seconds + static float GetMinLatency(u32 sample_rate, u32 buffer_size, u32 buffer_count); + static float GetMaxLatency(u32 sample_rate, u32 buffer_size, u32 buffer_count); + protected: virtual bool OpenDevice() = 0; virtual void PauseDevice(bool paused) = 0; virtual void CloseDevice() = 0; virtual void BufferAvailable() = 0; + ALWAYS_INLINE static SampleType ApplyVolume(SampleType sample, s32 volume) + { + return s16((s32(sample) * volume) / 100); + } + bool IsDeviceOpen() const { return (m_output_sample_rate > 0); } u32 GetSamplesAvailable() const; @@ -87,6 +98,9 @@ private: // TODO: Switch to semaphore std::condition_variable m_buffer_available_cv; + // volume, 0-100 + s32 m_output_volume = 100; + bool m_output_paused = true; bool m_sync = true; }; \ No newline at end of file