Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* 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:
virtual ~RawAudioStreamListener() = default;

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:
virtual ~WorkingAudioStreamListener() = default;

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* 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<float> 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))
, 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()){
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()
);
}



}
}
}
53 changes: 53 additions & 0 deletions SerialPrograms/Source/Kernels/AudioPipeline/Kernels_ChannelMixer.h
Original file line number Diff line number Diff line change
@@ -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 <vector>
#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<float> 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<float> m_weight_matrix;
AlignedVector<float> m_buffer;
ListenerSet<WorkingAudioStreamListener> m_listeners;
};




}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* 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{



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 <typename Type>
void RawAudio2WorkingAudio::set_converter(){
m_convert = [this](const void* data, size_t samples){
Kernels::AudioStreamConverter::convert_from<Type>(
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<uint8_t>();
break;
case SampleFormat::SINT16:
set_converter<int16_t>();
break;
case SampleFormat::SINT32:
set_converter<int32_t>();
break;
default:;
}

// Only allocate the interleaved buffer if we need to convert types.
if (m_convert != nullptr){
m_interleaved_floats = AlignedVector<float>(channels * buffer_size);
}

// Only allocate the split buffer if we need to split channels.
if (channels != 1){
m_split_floats = AlignedVector<float>(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;
}

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 * channels());
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;
data = (const char*)data + block * channels() * sample_size;
}
}



}
}
}
Loading
Loading