Bladeren bron

audio: first attempt at an audio streaming API

wip/image-kernel
Sam Hocevar 9 maanden geleden
bovenliggende
commit
4db95f5728
2 gewijzigde bestanden met toevoegingen van 225 en 0 verwijderingen
  1. +17
    -0
      include/lol/audio/stream
  2. +208
    -0
      include/lol/private/audio/stream.h

+ 17
- 0
include/lol/audio/stream Bestand weergeven

@@ -0,0 +1,17 @@
//
// Lol Engine
//
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net>
//
// Lol Engine is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What the Fuck You Want
// to Public License, Version 2, as published by the WTFPL Task Force.
// See http://www.wtfpl.net/ for more details.
//

#pragma once

#include "../private/push_macros.h"
#include "../private/audio/stream.h"
#include "../private/pop_macros.h"

+ 208
- 0
include/lol/private/audio/stream.h Bestand weergeven

@@ -0,0 +1,208 @@
//
// Lol Engine
//
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net>
//
// Lol Engine is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What the Fuck You Want
// to Public License, Version 2, as published by the WTFPL Task Force.
// See http://www.wtfpl.net/ for more details.
//

#pragma once

//
// The audio stream interface
// ——————————————————————————
// Stream, mix, and apply audio effects.
//

#include <functional> // std::function
#include <memory> // std::shared_ptr
#include <type_traits> // std::is_same_v
#include <unordered_set> // std::unordered_set

namespace lol::audio
{

template<typename T>
class stream
{
public:
using sample_type = T;

virtual size_t get(T* buf, size_t frames) = 0;

size_t channels() const { return m_channels; }

int frequency() const { return m_frequency; }

virtual ~stream() = default;

protected:
stream(size_t channels, int frequency)
: m_channels(channels),
m_frequency(frequency)
{}

size_t m_channels;
int m_frequency;
};

template<typename T>
class generator : public stream<T>
{
public:
generator(std::function<size_t(T*, size_t)> get, size_t channels, int frequency)
: stream<T>(channels, frequency),
m_get(get)
{}

virtual size_t get(T* buf, size_t frames) override
{
return m_get(buf, frames);
}

protected:
std::function<size_t(T*, size_t)> m_get;
};

template<typename T>
class mixer : public stream<T>
{
public:
mixer(size_t channels, int frequency)
: stream<T>(channels, frequency)
{}

void add(std::shared_ptr<stream<T>> s)
{
// FIXME: check the channel count!
m_streamers.insert(s);
}

void remove(std::shared_ptr<stream<T>> s)
{
m_streamers.erase(s);
}

virtual size_t get(T *buf, size_t frames) override
{
memset(buf, 0, frames * this->channels() * sizeof(T));

std::vector<T> tmp(frames * this->channels());
for (auto s : m_streamers)
{
s->get(tmp.data(), frames);
for (size_t i = 0; i < tmp.size(); ++i)
buf[i] += tmp[i];
}

return frames;
}

protected:
std::unordered_set<std::shared_ptr<stream<T>>> m_streamers;
};

template<typename T0, typename T>
static inline T convert_sample(T0);

template<>
inline float convert_sample(float x) { return x; }

template<>
inline float convert_sample(int16_t x) { return x / 32768.0f; }

template<>
inline float convert_sample(uint16_t x) { return x / 32767.0f - 1.0f; }

template<typename T, typename T0>
class converter : public stream<T>
{
public:
converter(std::shared_ptr<stream<T0>> s, size_t channels)
: stream<T>(channels, s->frequency()),
s0(s)
{}

virtual size_t get(T *buf, size_t frames) override
{
if constexpr(std::is_same_v<T0, T>)
if (this->channels() == s0->channels())
return s0->get(buf, frames);

std::vector<T0> tmp(frames * s0->channels());
s0->get(tmp.data(), frames);
for (size_t f = 0; f < frames; ++f)
{
buf[f * this->channels()] = tmp[f * s0->channels()];
for (size_t ch = 1; ch < this->channels(); ++ch)
{
size_t ch0 = std::max(size_t(0), s0->channels() - (this->channels() - ch));
buf[f * this->channels() + ch] = convert_sample<T0, T>(tmp[f * s0->channels() + ch0]);
}
}

return frames;
}

protected:
std::shared_ptr<stream<T0>> s0;
};

template<typename T>
class resampler : public stream<T>
{
public:
resampler(std::shared_ptr<stream<T>> s, int frequency)
: stream<T>(s->channels(), frequency),
s(s)
{}

virtual size_t get(T *buf, size_t frames) override
{
if (s->frequency() == this->frequency())
return s->get(buf, frames);

// FIXME: implement resampling!
memset(buf, 0, frames * this->channels() * sizeof(T));
return frames;
}

protected:
std::shared_ptr<stream<T>> s;
};

template<typename T>
static inline auto make_generator(std::function<size_t(T*, size_t)> f, size_t channels, int frequency)
{
return std::make_shared<generator<T>>(f, channels, frequency);
}

template<typename F>
static inline auto make_generator(F f, size_t channels, int frequency)
{
return make_generator(std::function(f), channels, frequency);
}

template<typename T, typename S0, typename T0 = S0::sample_type>
static inline auto make_converter(std::shared_ptr<S0> s, size_t channels)
{
return std::make_shared<converter<T, T0>>(std::shared_ptr<stream<T0>>(s), channels);
}

template<typename S, typename T = S::sample_type>
static inline auto make_resampler(std::shared_ptr<S> s, int frequency)
{
return std::make_shared<resampler<T>>(std::shared_ptr<stream<T>>(s), frequency);
}

template<typename T, typename S0, typename T0 = S0::sample_type>
static inline auto make_adapter(std::shared_ptr<S0> s, size_t channels, int frequency)
{
return make_resampler(make_converter<T>(s, channels), frequency);
}

} // namespace lol::audio

Laden…
Annuleren
Opslaan