From 1d286f0b6a6119d8a3bd9324f9f0497a88e50da6 Mon Sep 17 00:00:00 2001 From: Alexander Yee Date: Sun, 11 Oct 2026 15:17:04 -0700 Subject: [PATCH 1/2] Audio pipeline nodes. --- .../AudioPipeline/Kernels_AudioPipeline.h | 90 ++++++++++++ .../AudioPipeline/Kernels_ChannelMixer.cpp | 63 ++++++++ .../AudioPipeline/Kernels_ChannelMixer.h | 53 +++++++ .../Kernels_RawAudio2WorkingAudio.cpp | 128 ++++++++++++++++ .../Kernels_RawAudio2WorkingAudio.h | 57 ++++++++ .../Kernels_WorkingAudio2RawAudio.cpp | 138 ++++++++++++++++++ .../Kernels_WorkingAudio2RawAudio.h | 63 ++++++++ .../Kernels_AudioStreamConverter.cpp | 11 ++ SerialPrograms/cmake/SourceFiles.cmake | 7 + 9 files changed, 610 insertions(+) create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.h create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp create mode 100644 SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h new file mode 100644 index 0000000000..48c16c49ad --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h @@ -0,0 +1,90 @@ +/* Audio Pipeline + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_Kernels_AudioPipeline_H +#define PokemonAutomation_Kernels_AudioPipeline_H + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +enum class SampleFormat{ + UINT8, + SINT16, + SINT32, + FLOAT32, +}; + + + +class RawAudioStreamListener{ +public: + RawAudioStreamListener( + SampleFormat sample_type, + size_t sample_rate, + size_t channels + ) + : m_sample_type(sample_type) + , m_channels(channels) + , m_sample_rate(sample_rate) + {} + + SampleFormat sample_type() const{ + return m_sample_type; + } + size_t channels() const{ + return m_channels; + } + size_t sample_rate() const{ + return m_sample_rate; + } + + virtual void push_samples(size_t frames, const void* data) = 0; + + +private: + const SampleFormat m_sample_type; + const size_t m_channels; + const size_t m_sample_rate; +}; + + + +class WorkingAudioStreamListener{ +public: + WorkingAudioStreamListener( + size_t sample_rate, + size_t channels + ) + : m_channels(channels) + , m_sample_rate(sample_rate) + {} + + size_t channels() const{ + return m_channels; + } + size_t sample_rate() const{ + return m_sample_rate; + } + + virtual void push_samples(size_t samples, size_t stride, const float* data) = 0; + + +private: + const size_t m_channels; + const size_t m_sample_rate; +}; + + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp new file mode 100644 index 0000000000..7d2c050621 --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp @@ -0,0 +1,63 @@ +/* Audio Pipeline - ChannelMixer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Containers/AlignedVector.tpp" +#include "Kernels/AudioChannelMixer/Kernels_AudioChannelMixer.h" +#include "Kernels_ChannelMixer.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +ChannelMixer::~ChannelMixer() = default; +ChannelMixer::ChannelMixer( + size_t sample_rate, + size_t in_channels, + size_t out_channels, + std::vector weight_matrix, + size_t buffer_size +) + : WorkingAudioStreamListener(sample_rate, in_channels) + , m_in_channels(in_channels) + , m_out_channels(out_channels) + , m_buffer_size(buffer_size) + , m_weight_matrix(std::move(weight_matrix)) +{} + +void ChannelMixer::add_listener(WorkingAudioStreamListener& listener){ + if (sample_rate() != listener.sample_rate()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Sample rate doesn't match."); + } + if (m_out_channels != listener.channels()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Channel count doesn't match."); + } + m_listeners.add(listener); +} +void ChannelMixer::remove_listener(WorkingAudioStreamListener& listener){ + m_listeners.remove(listener); +} + +void ChannelMixer::push_samples(size_t samples, size_t stride, const float* data){ + AudioChannelMixer::mix_channels( + m_weight_matrix.data(), + samples, + m_out_channels, m_buffer_size, m_buffer.data(), + m_in_channels, stride, data + ); + m_listeners.run_method( + &WorkingAudioStreamListener::push_samples, + samples, m_buffer_size, m_buffer.data() + ); +} + + + +} +} +} diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h new file mode 100644 index 0000000000..212bd4399b --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h @@ -0,0 +1,53 @@ +/* Audio Pipeline - ChannelMixer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_Kernels_AudioPipeline_ChannelMixer_H +#define PokemonAutomation_Kernels_AudioPipeline_ChannelMixer_H + +#include +#include "Common/Cpp/ListenerSet.h" +#include "Common/Cpp/Containers/AlignedVector.h" +#include "Kernels_AudioPipeline.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +class ChannelMixer : public WorkingAudioStreamListener{ +public: + ~ChannelMixer(); + ChannelMixer( + size_t sample_rate, + size_t in_channels, + size_t out_channels, + std::vector weight_matrix, + size_t buffer_size = 4096 + 256 // 4k + break super-alignment + ); + + void add_listener(WorkingAudioStreamListener& listener); + void remove_listener(WorkingAudioStreamListener& listener); + + virtual void push_samples(size_t samples, size_t stride, const float* data) override; + + +private: + size_t m_in_channels; + size_t m_out_channels; + size_t m_buffer_size; + std::vector m_weight_matrix; + AlignedVector m_buffer; + ListenerSet m_listeners; +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp new file mode 100644 index 0000000000..d5a3099f6a --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp @@ -0,0 +1,128 @@ +/* Audio Pipeline - RawAudio2WorkingAudio + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Containers/AlignedVector.tpp" +#include "Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.h" +#include "Kernels/AudioChannelInterleave/Kernels_AudioChannelInterleave.h" +#include "Kernels_RawAudio2WorkingAudio.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +template +void RawAudio2WorkingAudio::set_converter(){ + m_convert = [this](const void* data, size_t samples){ + Kernels::AudioStreamConverter::convert_from( + m_interleaved_floats.data(), (const Type*)data, samples + ); + }; +} + +RawAudio2WorkingAudio::~RawAudio2WorkingAudio() = default; +RawAudio2WorkingAudio::RawAudio2WorkingAudio( + SampleFormat sample_type, + size_t sample_rate, + size_t channels, + size_t buffer_size +) + : RawAudioStreamListener(sample_type, sample_rate, channels) + , m_buffer_size(buffer_size) +{ + // Single-channel float is pass-through. + if (sample_type == SampleFormat::FLOAT32 && channels == 1){ + return; + } + + switch (sample_type){ + case SampleFormat::UINT8: + set_converter(); + break; + case SampleFormat::SINT16: + set_converter(); + break; + case SampleFormat::SINT32: + set_converter(); + break; + default:; + } + + // Only allocate the interleaved buffer if we need to convert types. + if (m_convert != nullptr){ + m_interleaved_floats = AlignedVector(channels * buffer_size); + } + + // Only allocate the split buffer if we need to split channels. + if (channels != 1){ + m_split_floats = AlignedVector(channels * buffer_size); + } +} + +void RawAudio2WorkingAudio::add_listener(WorkingAudioStreamListener& listener){ + if (sample_rate() != listener.sample_rate()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Sample rate doesn't match."); + } + if (channels() != listener.channels()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Channel count doesn't match."); + } + m_listeners.add(listener); +} +void RawAudio2WorkingAudio::remove_listener(WorkingAudioStreamListener& listener){ + m_listeners.remove(listener); +} + +void RawAudio2WorkingAudio::push_samples(size_t frames, const void* data){ + // Single-channel float pass-through. + if (sample_type() == SampleFormat::FLOAT32 && channels() == 1){ + m_listeners.run_method( + &WorkingAudioStreamListener::push_samples, + frames, 0, (const float*)data + ); + return; + } + + while (frames > 0){ + size_t block = std::min(frames, m_buffer_size); + + // Convert to float. (if needed) + const float* interleaved_floats; + if (m_convert){ + m_convert(data, block); + interleaved_floats = m_interleaved_floats.data(); + }else{ + interleaved_floats = (const float*)data; + } + + // Now split the channels. (if needed) + const float* split_floats; + if (channels() != 1){ + Kernels::AudioChannelInterleave::channel_deinterleave( + channels(), block, + m_buffer_size, m_split_floats.data(), + interleaved_floats + ); + split_floats = m_split_floats.data(); + }else{ + split_floats = interleaved_floats; + } + + m_listeners.run_method( + &WorkingAudioStreamListener::push_samples, + block, m_buffer_size, split_floats + ); + + frames -= block; + } +} + + + +} +} +} diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.h new file mode 100644 index 0000000000..9e816043f4 --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.h @@ -0,0 +1,57 @@ +/* Audio Pipeline - RawAudio2WorkingAudio + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_Kernels_AudioPipeline_RawAudio2WorkingAudio_H +#define PokemonAutomation_Kernels_AudioPipeline_RawAudio2WorkingAudio_H + +#include +#include "Common/Cpp/ListenerSet.h" +#include "Common/Cpp/Containers/AlignedVector.h" +#include "Kernels_AudioPipeline.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +class RawAudio2WorkingAudio : public RawAudioStreamListener{ +public: + ~RawAudio2WorkingAudio(); + RawAudio2WorkingAudio( + SampleFormat sample_type, + size_t sample_rate, + size_t channels, + size_t buffer_size = 4096 + 256 // 4k + break super-alignment + ); + + void add_listener(WorkingAudioStreamListener& listener); + void remove_listener(WorkingAudioStreamListener& listener); + + virtual void push_samples(size_t frames, const void* data) override; + + +private: + template + void set_converter(); + + +private: + size_t m_buffer_size; + std::function m_convert = nullptr; + + AlignedVector m_interleaved_floats; + AlignedVector m_split_floats; + ListenerSet m_listeners; +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp new file mode 100644 index 0000000000..a44562d128 --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp @@ -0,0 +1,138 @@ +/* Audio Pipeline - Kernels_WorkingAudio2RawAudio + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Containers/AlignedVector.tpp" +#include "Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.h" +#include "Kernels/AudioChannelInterleave/Kernels_AudioChannelInterleave.h" +#include "Kernels_WorkingAudio2RawAudio.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +template +size_t WorkingAudio2RawAudio::set_converter(){ + m_convert = [this](const void* data, size_t samples){ + Kernels::AudioStreamConverter::convert_to( + m_interleaved_floats.data(), (Type*)m_interleaved_raw.data(), samples, + m_multiplier.load(std::memory_order_acquire) + ); + }; + return sizeof(Type); +} + + +WorkingAudio2RawAudio::~WorkingAudio2RawAudio() = default; +WorkingAudio2RawAudio::WorkingAudio2RawAudio( + SampleFormat sample_type, + size_t sample_rate, + size_t channels, + float multiplier, + size_t buffer_size +) + : WorkingAudioStreamListener(sample_rate, channels) + , m_sample_type(sample_type) + , m_multiplier(multiplier) + , m_buffer_size(buffer_size) +{ + // Single-channel float is pass-through. + if (sample_type == SampleFormat::FLOAT32 && channels == 1){ + return; + } + + size_t sample_size = sizeof(float); + switch (sample_type){ + case SampleFormat::UINT8: + sample_size = set_converter(); + break; + case SampleFormat::SINT16: + sample_size = set_converter(); + break; + case SampleFormat::SINT32: + sample_size = set_converter(); + break; + default:; + } + + // Only allocate the interleaved buffer if we need to convert types. + if (m_convert != nullptr){ + m_interleaved_raw = AlignedVector(channels * buffer_size * sample_size); + } + + // Only allocate the split buffer if we need to split channels. + if (channels != 1){ + m_interleaved_floats = AlignedVector(channels * buffer_size); + } +} + +void WorkingAudio2RawAudio::add_listener(RawAudioStreamListener& listener){ + if (m_sample_type != listener.sample_type()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Sample type doesn't match."); + } + if (sample_rate() != listener.sample_rate()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Sample rate doesn't match."); + } + if (channels() != listener.channels()){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Channel count doesn't match."); + } + m_listeners.add(listener); +} +void WorkingAudio2RawAudio::remove_listener(RawAudioStreamListener& listener){ + m_listeners.remove(listener); +} + +void WorkingAudio2RawAudio::push_samples(size_t samples, size_t stride, const float* data){ + // Single-channel float pass-through. + if (m_sample_type == SampleFormat::FLOAT32 && channels() == 1){ + m_listeners.run_method( + &RawAudioStreamListener::push_samples, + samples, (const float*)data + ); + return; + } + + while (samples > 0){ + size_t block = std::min(samples, m_buffer_size); + + // Combine the channels. (if needed) + const float* interleaved_floats; + if (channels() != 1){ + Kernels::AudioChannelInterleave::channel_interleave( + channels(), block, + stride, data, + m_interleaved_floats.data() + ); + interleaved_floats = m_interleaved_floats.data(); + }else{ + interleaved_floats = data; + } + + // Convert to output type. (if needed) + const void* interleaved_raw; + if (m_convert){ + m_convert(interleaved_floats, block); + interleaved_raw = m_interleaved_raw.data(); + }else{ + interleaved_raw = interleaved_floats; + } + + m_listeners.run_method( + &RawAudioStreamListener::push_samples, + block, interleaved_raw + ); + + samples -= block; + } +} + + + +} +} +} diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h new file mode 100644 index 0000000000..75700b784b --- /dev/null +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h @@ -0,0 +1,63 @@ +/* Audio Pipeline - Kernels_WorkingAudio2RawAudio + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_Kernels_AudioPipeline_WorkingAudio2RawAudio_H +#define PokemonAutomation_Kernels_AudioPipeline_WorkingAudio2RawAudio_H + +#include +#include "Common/Cpp/ListenerSet.h" +#include "Common/Cpp/Containers/AlignedVector.h" +#include "Kernels_AudioPipeline.h" + +namespace PokemonAutomation{ +namespace Kernels{ +namespace AudioPipeline{ + + + +class WorkingAudio2RawAudio : public WorkingAudioStreamListener{ +public: + ~WorkingAudio2RawAudio(); + WorkingAudio2RawAudio( + SampleFormat sample_type, + size_t sample_rate, + size_t channels, + float multiplier, + size_t buffer_size = 4096 + 256 // 4k + break super-alignment + ); + + void add_listener(RawAudioStreamListener& listener); + void remove_listener(RawAudioStreamListener& listener); + + void set_multiplier(float multiplier); + + virtual void push_samples(size_t samples, size_t stride, const float* data) override; + + +private: + template + size_t set_converter(); + + +private: + SampleFormat m_sample_type; + std::atomic m_multiplier; + size_t m_buffer_size; + std::function m_convert = nullptr; + + AlignedVector m_interleaved_raw; + AlignedVector m_interleaved_floats; + ListenerSet m_listeners; +}; + + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.cpp b/SerialPrograms/Source/Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.cpp index 5b9727a549..de72f612d9 100644 --- a/SerialPrograms/Source/Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.cpp +++ b/SerialPrograms/Source/Kernels/AudioStreamConverter/Kernels_AudioStreamConverter.cpp @@ -97,6 +97,17 @@ void convert_to(c_ptr native, r_ptr ext, size_t samples, fl +template void convert_from(r_ptr native, c_ptr ext, size_t samples); +template void convert_from(r_ptr native, c_ptr ext, size_t samples); +template void convert_from(r_ptr native, c_ptr ext, size_t samples); +template void convert_to(c_ptr native, r_ptr ext, size_t samples, float multiplier); +template void convert_to(c_ptr native, r_ptr ext, size_t samples, float multiplier); +template void convert_to(c_ptr native, r_ptr ext, size_t samples, float multiplier); + + + + + void add_tests(UnitTestDatabase& database){ add_tests_Default(database); #ifdef PA_AutoDispatch_x64_08_Nehalem diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 5893eb0f1d..397ef04db3 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1017,6 +1017,13 @@ file(GLOB LIBRARY_SOURCES Source/Kernels/AudioChannelMixer/Kernels_AudioChannelMixer_x64_AVX512.cpp Source/Kernels/AudioChannelMixer/Kernels_AudioChannelMixer_x64_FMA3.cpp Source/Kernels/AudioChannelMixer/Kernels_AudioChannelMixer_x64_SSE.cpp + Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h + Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp + Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h + Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp + Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.h + Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp + Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h Source/Kernels/AudioResampler/Kernels_AudioResampler_BlockKernel_Default.h Source/Kernels/AudioResampler/Kernels_AudioResampler_BlockKernel_x64_SSE2.h Source/Kernels/AudioResampler/Kernels_AudioResampler_BlockKernel_x64_FMA3.h From 0fce742e3fa389db004268605f1080d43bb5385c Mon Sep 17 00:00:00 2001 From: Gin890 <79114795+Gin890@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:12:53 -0700 Subject: [PATCH 2/2] AudioPipeline: fix unallocated m_buffer, missing set_multiplier, and multi-channel conversion (#1566) * AudioPipeline: allocate ChannelMixer m_buffer, define set_multiplier, validate weight matrix, add virtual listener destructors. * AudioPipeline: fix unallocated m_buffer, missing set_multiplier, and multi-channel conversion. - ChannelMixer: allocate m_buffer (out_channels * buffer_size). push_samples passed the empty AlignedVector's null pointer to mix_channels as the output buffer. - WorkingAudio2RawAudio: define set_multiplier() with an atomic release store (it was declared but never defined, a link error waiting to happen). Include where the std::atomic member is declared. - ChannelMixer: validate weight_matrix size in the constructor, throwing the same InternalProgramError add_listener() uses. - RawAudioStreamListener / WorkingAudioStreamListener: virtual destructors, since the pipeline framework will hold these as base pointers. - RawAudio2WorkingAudio / WorkingAudio2RawAudio: convert block*channels() samples instead of block; the interleaved buffers hold one sample per channel, so multi-channel non-float audio was only partially converted. - WorkingAudio2RawAudio::set_converter: read the interleaved float source from the 'data' argument instead of m_interleaved_floats.data(), which is never allocated for single-channel output (null read). * AudioPipeline: advance source pointer between chunks in converter push_samples. - RawAudio2WorkingAudio::push_samples and WorkingAudio2RawAudio::push_samples never advanced their source pointer between chunks, so any push of more samples than the buffer re-processed the same first block on every iteration of the loop. - RawAudio2WorkingAudio advances by block*channels()*sizeof(sample); WorkingAudio2RawAudio advances by block*stride. --- .../AudioPipeline/Kernels_AudioPipeline.h | 4 ++++ .../AudioPipeline/Kernels_ChannelMixer.cpp | 7 +++++- .../Kernels_RawAudio2WorkingAudio.cpp | 23 ++++++++++++++++++- .../Kernels_WorkingAudio2RawAudio.cpp | 8 +++++-- .../Kernels_WorkingAudio2RawAudio.h | 1 + 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h index 48c16c49ad..b20d490f55 100644 --- a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_AudioPipeline.h @@ -24,6 +24,8 @@ enum class SampleFormat{ class RawAudioStreamListener{ public: + virtual ~RawAudioStreamListener() = default; + RawAudioStreamListener( SampleFormat sample_type, size_t sample_rate, @@ -57,6 +59,8 @@ class RawAudioStreamListener{ class WorkingAudioStreamListener{ public: + virtual ~WorkingAudioStreamListener() = default; + WorkingAudioStreamListener( size_t sample_rate, size_t channels diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp index 7d2c050621..81e5a5c5b0 100644 --- a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.cpp @@ -28,7 +28,12 @@ ChannelMixer::ChannelMixer( , m_out_channels(out_channels) , m_buffer_size(buffer_size) , m_weight_matrix(std::move(weight_matrix)) -{} + , m_buffer(out_channels * buffer_size) +{ + if (m_weight_matrix.size() != in_channels * out_channels){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Weight matrix size doesn't match."); + } +} void ChannelMixer::add_listener(WorkingAudioStreamListener& listener){ if (sample_rate() != listener.sample_rate()){ diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp index d5a3099f6a..881250bfdc 100644 --- a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_RawAudio2WorkingAudio.cpp @@ -16,6 +16,25 @@ namespace AudioPipeline{ +namespace{ + + +size_t sample_format_size(SampleFormat sample_type){ + switch (sample_type){ + case SampleFormat::UINT8: return sizeof(uint8_t); + case SampleFormat::SINT16: return sizeof(int16_t); + case SampleFormat::SINT32: return sizeof(int32_t); + case SampleFormat::FLOAT32: return sizeof(float); + default:; + } + return 0; +} + + +} + + + template void RawAudio2WorkingAudio::set_converter(){ m_convert = [this](const void* data, size_t samples){ @@ -87,13 +106,14 @@ void RawAudio2WorkingAudio::push_samples(size_t frames, const void* data){ return; } + const size_t sample_size = sample_format_size(sample_type()); while (frames > 0){ size_t block = std::min(frames, m_buffer_size); // Convert to float. (if needed) const float* interleaved_floats; if (m_convert){ - m_convert(data, block); + m_convert(data, block * channels()); interleaved_floats = m_interleaved_floats.data(); }else{ interleaved_floats = (const float*)data; @@ -118,6 +138,7 @@ void RawAudio2WorkingAudio::push_samples(size_t frames, const void* data){ ); frames -= block; + data = (const char*)data + block * channels() * sample_size; } } diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp index a44562d128..b873401e5b 100644 --- a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.cpp @@ -20,7 +20,7 @@ template size_t WorkingAudio2RawAudio::set_converter(){ m_convert = [this](const void* data, size_t samples){ Kernels::AudioStreamConverter::convert_to( - m_interleaved_floats.data(), (Type*)m_interleaved_raw.data(), samples, + (const float*)data, (Type*)m_interleaved_raw.data(), samples, m_multiplier.load(std::memory_order_acquire) ); }; @@ -86,6 +86,9 @@ void WorkingAudio2RawAudio::add_listener(RawAudioStreamListener& listener){ void WorkingAudio2RawAudio::remove_listener(RawAudioStreamListener& listener){ m_listeners.remove(listener); } +void WorkingAudio2RawAudio::set_multiplier(float multiplier){ + m_multiplier.store(multiplier, std::memory_order_release); +} void WorkingAudio2RawAudio::push_samples(size_t samples, size_t stride, const float* data){ // Single-channel float pass-through. @@ -116,7 +119,7 @@ void WorkingAudio2RawAudio::push_samples(size_t samples, size_t stride, const fl // Convert to output type. (if needed) const void* interleaved_raw; if (m_convert){ - m_convert(interleaved_floats, block); + m_convert(interleaved_floats, block * channels()); interleaved_raw = m_interleaved_raw.data(); }else{ interleaved_raw = interleaved_floats; @@ -128,6 +131,7 @@ void WorkingAudio2RawAudio::push_samples(size_t samples, size_t stride, const fl ); samples -= block; + data += block * stride; } } diff --git a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h index 75700b784b..cb22b9dd2c 100644 --- a/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h +++ b/SerialPrograms/Source/Kernels/AudioPipeline/Kernels_WorkingAudio2RawAudio.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_Kernels_AudioPipeline_WorkingAudio2RawAudio_H #define PokemonAutomation_Kernels_AudioPipeline_WorkingAudio2RawAudio_H +#include #include #include "Common/Cpp/ListenerSet.h" #include "Common/Cpp/Containers/AlignedVector.h"