Make audio channel a separate class

This commit is contained in:
Nikita Lisitsa 2022-10-05 22:39:54 +03:00
parent cab34558ce
commit 26b44a9fa3
4 changed files with 81 additions and 56 deletions

View file

@ -5,6 +5,8 @@
#include <psemek/audio/wave/square.hpp> #include <psemek/audio/wave/square.hpp>
#include <psemek/audio/wave/triangle.hpp> #include <psemek/audio/wave/triangle.hpp>
#include <psemek/audio/effect/volume.hpp> #include <psemek/audio/effect/volume.hpp>
#include <psemek/audio/effect/fade_in.hpp>
#include <psemek/audio/effect/fade_out.hpp>
#include <psemek/audio/duplicate.hpp> #include <psemek/audio/duplicate.hpp>
#include <psemek/audio/stereo.hpp> #include <psemek/audio/stereo.hpp>
#include <psemek/audio/mixer.hpp> #include <psemek/audio/mixer.hpp>
@ -19,6 +21,16 @@ using namespace psemek;
static std::map<SDL_Keycode, int> const key_to_midi static std::map<SDL_Keycode, int> const key_to_midi
{ {
{SDLK_z, 59},
{SDLK_x, 60},
{SDLK_c, 61},
{SDLK_v, 62},
{SDLK_b, 63},
{SDLK_n, 64},
{SDLK_m, 65},
{SDLK_COMMA, 66},
{SDLK_PERIOD, 67},
{SDLK_SLASH, 68},
{SDLK_a, 69}, {SDLK_a, 69},
{SDLK_s, 70}, {SDLK_s, 70},
{SDLK_d, 71}, {SDLK_d, 71},
@ -30,6 +42,18 @@ static std::map<SDL_Keycode, int> const key_to_midi
{SDLK_l, 77}, {SDLK_l, 77},
{SDLK_SEMICOLON, 78}, {SDLK_SEMICOLON, 78},
{SDLK_QUOTE, 79}, {SDLK_QUOTE, 79},
{SDLK_q, 80},
{SDLK_w, 81},
{SDLK_e, 82},
{SDLK_r, 83},
{SDLK_t, 84},
{SDLK_y, 85},
{SDLK_u, 86},
{SDLK_i, 87},
{SDLK_o, 88},
{SDLK_p, 89},
{SDLK_LEFTBRACKET, 90},
{SDLK_RIGHTBRACKET, 91},
}; };
struct audio_app struct audio_app
@ -41,8 +65,8 @@ struct audio_app
mixer_ = audio::make_mixer(); mixer_ = audio::make_mixer();
auto [ dup1, dup2 ] = audio::duplicate(mixer_); auto [ dup1, dup2 ] = audio::duplicate(mixer_);
left_volume_ = audio::volume(dup1, 0.f, 0.1f); left_volume_ = audio::volume(dup1, 0.5f, 0.1f);
right_volume_ = audio::volume(dup2, 0.f, 0.1f); right_volume_ = audio::volume(dup2, 0.5f, 0.1f);
auto result = audio::stereo(left_volume_, right_volume_); auto result = audio::stereo(left_volume_, right_volume_);
engine_.output(result); engine_.output(result);
} }
@ -55,7 +79,7 @@ struct audio_app
{ {
int midi = key_to_midi.at(key); int midi = key_to_midi.at(key);
auto tone = audio::sine_wave(440.f * std::pow(2.f, (midi - 69) / 12.f)); auto tone = audio::sine_wave(440.f * std::pow(2.f, (midi - 69) / 12.f));
channels_[key] = mixer_->add(tone); channels_[key] = mixer_->add(audio::fade_in(tone, 0.01f));
} }
} }
@ -65,7 +89,8 @@ struct audio_app
if (channels_.contains(key)) if (channels_.contains(key))
{ {
channels_[key]->stop(); auto & ch = channels_[key];
ch->stream(audio::fade_out(ch->stream(), 0.01f));
channels_.erase(key); channels_.erase(key);
} }
} }
@ -83,7 +108,7 @@ private:
audio::engine engine_; audio::engine engine_;
audio::mixer_ptr mixer_; audio::mixer_ptr mixer_;
std::shared_ptr<audio::volume_control> left_volume_, right_volume_; std::shared_ptr<audio::volume_control> left_volume_, right_volume_;
std::map<SDL_Keycode, audio::mixer::channel_ptr> channels_; std::map<SDL_Keycode, audio::channel_ptr> channels_;
util::clock<> clock_; util::clock<> clock_;
}; };

View file

@ -0,0 +1,43 @@
#pragma once
#include <psemek/audio/stream.hpp>
#include <atomic>
#include <memory>
namespace psemek::audio
{
struct channel
{
channel(stream_ptr stream)
: stream_(std::move(stream))
{}
stream_ptr stream() const
{
return std::atomic_load(&stream_);
}
stream_ptr stream(stream_ptr new_stream)
{
return std::atomic_exchange(&stream_, std::move(new_stream));
}
stream_ptr stop()
{
return stream(nullptr);
}
bool is_stopped() const
{
return stream() != nullptr;
}
private:
stream_ptr stream_;
};
using channel_ptr = std::shared_ptr<channel>;
}

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <psemek/audio/stream.hpp> #include <psemek/audio/stream.hpp>
#include <psemek/util/function.hpp> #include <psemek/audio/channel.hpp>
#include <memory> #include <memory>
@ -11,19 +11,6 @@ namespace psemek::audio
struct mixer struct mixer
: stream : stream
{ {
struct channel
{
virtual void stop() = 0;
virtual bool is_stopped() const = 0;
virtual void replace(util::function<stream_ptr(stream_ptr)> modifier) = 0;
virtual ~channel() {}
};
using channel_ptr = std::shared_ptr<channel>;
virtual channel_ptr add(stream_ptr stream) = 0; virtual channel_ptr add(stream_ptr stream) = 0;
}; };

View file

@ -10,35 +10,6 @@ namespace psemek::audio
namespace namespace
{ {
struct channel_impl final
: mixer::channel
{
stream_ptr stream;
void stop() override;
bool is_stopped() const override;
void replace(util::function<stream_ptr(stream_ptr)> modifier) override;
};
void channel_impl::stop()
{
std::atomic_store(&stream, stream_ptr());
}
bool channel_impl::is_stopped() const
{
return std::atomic_load(&stream) != nullptr;
}
void channel_impl::replace(util::function<stream_ptr(stream_ptr)> modifier)
{
stream_ptr stream = std::atomic_load(&(this->stream));
stream = modifier(std::move(stream));
std::atomic_store(&(this->stream), std::move(stream));
}
struct mixer_impl final struct mixer_impl final
: mixer : mixer
, std::enable_shared_from_this<mixer_impl> , std::enable_shared_from_this<mixer_impl>
@ -48,19 +19,18 @@ namespace psemek::audio
std::size_t read(float * data, std::size_t sample_count) override; std::size_t read(float * data, std::size_t sample_count) override;
private: private:
std::vector<std::shared_ptr<channel_impl>> channels_; std::vector<channel_ptr> channels_;
std::vector<std::shared_ptr<channel_impl>> alive_channels_; std::vector<channel_ptr> alive_channels_;
std::vector<float> buffer_; std::vector<float> buffer_;
std::mutex new_channels_mutex_; std::mutex new_channels_mutex_;
std::vector<std::shared_ptr<channel_impl>> new_channels_; std::vector<channel_ptr> new_channels_;
}; };
mixer::channel_ptr mixer_impl::add(stream_ptr stream) channel_ptr mixer_impl::add(stream_ptr stream)
{ {
auto result = std::make_shared<channel_impl>(); auto result = std::make_shared<channel>(std::move(stream));
result->stream = std::move(stream);
{ {
std::lock_guard lock{new_channels_mutex_}; std::lock_guard lock{new_channels_mutex_};
@ -73,7 +43,7 @@ namespace psemek::audio
std::size_t mixer_impl::read(float * data, std::size_t sample_count) std::size_t mixer_impl::read(float * data, std::size_t sample_count)
{ {
{ {
std::vector<std::shared_ptr<channel_impl>> new_channels; std::vector<channel_ptr> new_channels;
{ {
std::lock_guard lock{new_channels_mutex_}; std::lock_guard lock{new_channels_mutex_};
new_channels = std::move(new_channels_); new_channels = std::move(new_channels_);
@ -88,7 +58,7 @@ namespace psemek::audio
for (auto & ch : channels_) for (auto & ch : channels_)
{ {
auto stream = std::atomic_load(&(ch->stream)); auto stream = ch->stream();
if (!stream) if (!stream)
continue; continue;