Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pcmDriver stop methods #270

Merged
merged 11 commits into from
Nov 5, 2023
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
endif()


project ("sdl2-hyper-sonic-drivers" VERSION 0.13.2 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards")
project ("sdl2-hyper-sonic-drivers" VERSION 0.13.3 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards")
include (TestBigEndian)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
if(IS_BIG_ENDIAN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,9 @@ namespace HyperSonicDrivers::audio::sdl2
int16_t* buf = std::bit_cast<int16_t*>(samples);
// we store stereo, 16-bit samples (div 2 for stereo and 2 from 8 to 16 bits)
assert(len % 4 == 0);
len >>= 1;
// zero the buf (size of 2ch stereo: len*2 of 16 bits)
memset(buf, 0, len * sizeof(int16_t));
len >>= 1; // size of the stereo 16 bits buffer.

memset(buf, 0, len);
len >>= 2;
// mix all channels
size_t res = 0;
for (const auto& ch : m_channels)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace HyperSonicDrivers::audio::streams
return m_curPos == m_sound->dataSize;
}

std::weak_ptr<PCMSound> PCMStream::getSound() const noexcept
std::shared_ptr<PCMSound> PCMStream::getSound() const noexcept
{
return m_sound;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace HyperSonicDrivers::audio::streams
uint32_t getRate() const override;
bool endOfData() const override;

std::weak_ptr<PCMSound> getSound() const noexcept;
std::shared_ptr<PCMSound> getSound() const noexcept;
private:
std::shared_ptr<PCMSound> m_sound;
uint32_t m_curPos = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ namespace HyperSonicDrivers::drivers
PCMDriver::PCMDriver(const std::shared_ptr<audio::IMixer>& mixer, const uint8_t max_channels) :
max_streams(std::min(mixer->max_channels, max_channels)), m_mixer(mixer)
{
m_PCMStreams.resize(max_streams);
}

bool PCMDriver::isPlaying() const noexcept
{
for(const auto& ss: m_PCMStreams)
for (const auto& [stream, _] : m_PCMStreams_channels)
{
if (isPCMStreamPlaying_(ss))
if (isPCMStreamPlaying_(stream))
return true;
}

Expand All @@ -24,48 +23,97 @@ namespace HyperSonicDrivers::drivers

bool PCMDriver::isPlaying(const std::shared_ptr<audio::PCMSound>& sound) const noexcept
{
// TODO:
// should map channelId to check directly in the mixer?
// how to find a free slot then?
// does we need to really track it?
// probably using a map instead of a vector is ok,
// no need to define nether max-channels.
// but that is because if wanting to reserve some channels for something
// else that is not PCM related...
// anyway... it could be achieved having the mixer a "lock or reserved channel"
// feature or something that that one won't be used unless
// it is for the resources that has been reserved for.....
for(const auto& ss : m_PCMStreams)
for (const auto& [stream, _] : m_PCMStreams_channels)
{
if (ss->getSound().lock() == sound)
return isPCMStreamPlaying_(ss);
if (stream->getSound() == sound)
return isPCMStreamPlaying_(stream);
}

return false;
}

std::optional<uint8_t> PCMDriver::play(const std::shared_ptr<audio::PCMSound>& sound, const uint8_t volume, const int8_t pan)
{
// find first free slot
auto it = std::ranges::find_if_not(m_PCMStreams, isPCMStreamPlaying_);
if (it == m_PCMStreams.end())
releaseEndedStreams_();
if (m_PCMStreams_channels.size() == max_streams)
return std::nullopt;

*it = std::make_shared<PCMStream>(sound);
auto s = std::make_shared<PCMStream>(sound);

auto channelId = m_mixer->play(
sound->group,
*it,
s,
volume,
pan
);

if (!channelId.has_value())
*it = nullptr;
if (channelId.has_value())
m_PCMStreams_channels[s] = channelId.value();

return channelId;
}

void PCMDriver::stop(const uint8_t channel_id, const bool releaseEndedStreams) noexcept
{
auto it = std::ranges::find_if(
m_PCMStreams_channels,
[channel_id](std::pair<const std::shared_ptr<audio::streams::PCMStream>&, const int> p) {
return channel_id == p.second;
}
);

if (it == m_PCMStreams_channels.end())
return;

if (!(it->first)->isEnded())
m_mixer->reset(channel_id);

if (releaseEndedStreams)
{
m_PCMStreams_channels.erase(it);
releaseEndedStreams_();
}
}

void PCMDriver::stop(const std::shared_ptr<audio::PCMSound>& sound, const bool releaseEndedStreams)
{
auto it = std::ranges::find_if(
m_PCMStreams_channels,
[&sound](std::pair<const std::shared_ptr<audio::streams::PCMStream>&, const int> p) {
return p.first != nullptr && sound == p.first->getSound();
}
);

if (it == m_PCMStreams_channels.end())
return;

stop(it->second, releaseEndedStreams);
}

void PCMDriver::stop() noexcept
{
for (const auto& [_, ch_id] : m_PCMStreams_channels)
stop(ch_id, false);

releaseStreams_();
}

void PCMDriver::releaseEndedStreams_() noexcept
{
for (auto it = m_PCMStreams_channels.begin(); it != m_PCMStreams_channels.end();)
{
if (!isPCMStreamPlaying_(it->first))
it = m_PCMStreams_channels.erase(it);
else
++it;
}
}

void PCMDriver::releaseStreams_() noexcept
{
m_PCMStreams_channels.clear();
}

inline bool PCMDriver::isPCMStreamPlaying_(const std::shared_ptr<audio::streams::PCMStream>& stream) noexcept
{
return stream != nullptr && !stream->isEnded();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <memory>
#include <cstdint>
#include <vector>
#include <memory>
#include <map>
#include <optional>
#include <HyperSonicDrivers/audio/IMixer.hpp>
#include <HyperSonicDrivers/audio/streams/PCMStream.hpp>
Expand Down Expand Up @@ -30,11 +30,18 @@ namespace HyperSonicDrivers::drivers
const uint8_t volume = audio::mixer::Channel_max_volume,
const int8_t pan = 0
);
void stop(const uint8_t channel_id, const bool releaseEndedStreams = true) noexcept;
void stop(const std::shared_ptr<audio::PCMSound>& sound, const bool releaseEndedStreams = true);
void stop() noexcept;


const uint8_t max_streams;
private:
std::shared_ptr<audio::IMixer> m_mixer;
std::vector<std::shared_ptr<audio::streams::PCMStream>> m_PCMStreams;
std::map<std::shared_ptr<audio::streams::PCMStream>, uint8_t> m_PCMStreams_channels;

void releaseEndedStreams_() noexcept;
void releaseStreams_() noexcept;

static bool isPCMStreamPlaying_(const std::shared_ptr<audio::streams::PCMStream>& stream) noexcept;
};
Expand Down
11 changes: 9 additions & 2 deletions sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,15 @@ macro_test(
)

macro_test(
EXE TestIMusicDriver
FILES "drivers/TestIMusicDriver.cpp"
EXE TestIAudioDriver
FILES "drivers/TestIAudioDriver.cpp"
LINKS_PRIVATE hyper-sonic-drivers-static
FIXTURES
)

macro_test(
EXE TestPCMDriver
FILES "drivers/TestPCMDriver.cpp"
LINKS_PRIVATE hyper-sonic-drivers-static
FIXTURES
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace HyperSonicDrivers::audio
{
public:
int rate = 44100;
uint8_t cur_ch = 255;

IMixerMock() : IMixer(32, 44100, 1024) {};
explicit IMixerMock(const int freq) : IMixer(32, freq, 1024) {};
Expand All @@ -24,7 +25,7 @@ namespace HyperSonicDrivers::audio
const uint8_t vol,
const int8_t pan
) override {
return std::make_optional(0);
return std::make_optional((++cur_ch) % max_channels);
};

void suspend() noexcept override {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

namespace HyperSonicDrivers::drivers
{
class IMusicDriverMock : public IAudioDriver
class IAudioDriverMock : public IAudioDriver
{
public:
IMusicDriverMock(const std::shared_ptr<devices::IDevice>& device) : IAudioDriver(device) {}
IAudioDriverMock(const std::shared_ptr<devices::IDevice>& device) : IAudioDriver(device) {}
void play(const uint8_t track) noexcept override {};
void stop() noexcept override {};
bool isPlaying() const noexcept override { return false; };
};

TEST(IAudioDriver, cstor_nullptr)
{
EXPECT_THROW(IMusicDriverMock md(nullptr), std::runtime_error);
EXPECT_THROW(IAudioDriverMock md(nullptr), std::runtime_error);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <HyperSonicDrivers/drivers/PCMDriver.hpp>
#include <HyperSonicDrivers/audio/IMixerMock.hpp>
#include <HyperSonicDrivers/audio/PCMSound.hpp>
#include <HyperSonicDrivers/audio/mixer/ChannelGroup.hpp>
#include <memory>
#include <cstdint>

namespace HyperSonicDrivers::drivers
{
TEST(PCMDriver, play_stop0)
{
auto mixer = audio::make_mixer<audio::IMixerMock>();
auto drv = PCMDriver(mixer);

EXPECT_EQ(drv.max_streams, mixer->max_channels);

auto sound_data = std::make_shared<int16_t[]>(1);
auto sound = std::make_shared<audio::PCMSound>(audio::mixer::eChannelGroup::Plain, true, 44100, 1, sound_data);
auto ch_id = drv.play(sound);

ASSERT_TRUE(drv.isPlaying(sound));
ASSERT_TRUE(drv.isPlaying());
ASSERT_TRUE(ch_id.has_value());
EXPECT_EQ(ch_id.value(), 0);

drv.stop(ch_id.value());
EXPECT_FALSE(drv.isPlaying(sound));
EXPECT_FALSE(drv.isPlaying());
}

TEST(PCMDriver, play_stop1)
{
auto mixer = audio::make_mixer<audio::IMixerMock>();
auto drv = PCMDriver(mixer);

EXPECT_EQ(drv.max_streams, mixer->max_channels);

auto sound_data = std::make_shared<int16_t[]>(1);
auto sound = std::make_shared<audio::PCMSound>(audio::mixer::eChannelGroup::Plain, true, 44100, 1, sound_data);
auto ch_id = drv.play(sound);

ASSERT_TRUE(drv.isPlaying(sound));
ASSERT_TRUE(drv.isPlaying());
ASSERT_TRUE(ch_id.has_value());
EXPECT_EQ(ch_id.value(), 0);

drv.stop(sound);
EXPECT_FALSE(drv.isPlaying(sound));
EXPECT_FALSE(drv.isPlaying());
}

TEST(PCMDriver, play_stop2)
{
auto mixer = audio::make_mixer<audio::IMixerMock>();
auto drv = PCMDriver(mixer);

EXPECT_EQ(drv.max_streams, mixer->max_channels);

auto sound_data = std::make_shared<int16_t[]>(1);
auto sound = std::make_shared<audio::PCMSound>(audio::mixer::eChannelGroup::Plain, true, 44100, 1, sound_data);
auto ch_id = drv.play(sound);

ASSERT_TRUE(drv.isPlaying(sound));
ASSERT_TRUE(drv.isPlaying());
ASSERT_TRUE(ch_id.has_value());
EXPECT_EQ(ch_id.value(), 0);

drv.stop();
EXPECT_FALSE(drv.isPlaying(sound));
EXPECT_FALSE(drv.isPlaying());
}

TEST(PCMDriver, play_stop_complex)
{
auto mixer = audio::make_mixer<audio::IMixerMock>();
auto drv = PCMDriver(mixer);

EXPECT_EQ(drv.max_streams, mixer->max_channels);

auto sound_data = std::make_shared<int16_t[]>(1);
auto sound = std::make_shared<audio::PCMSound>(audio::mixer::eChannelGroup::Plain, true, 44100, 1, sound_data);
auto sound_data2 = std::make_shared<int16_t[]>(1);
auto sound2 = std::make_shared<audio::PCMSound>(audio::mixer::eChannelGroup::Unknown, true, 22050, 1, sound_data2);

auto ch_id = drv.play(sound);
EXPECT_TRUE(drv.play(sound2).has_value());
EXPECT_TRUE(drv.play(sound2).has_value());
EXPECT_TRUE(drv.play(sound2).has_value());

ASSERT_TRUE(drv.isPlaying(sound));
ASSERT_TRUE(drv.isPlaying());
ASSERT_TRUE(ch_id.has_value());
EXPECT_EQ(ch_id.value(), 0);

drv.stop(ch_id.value());
EXPECT_FALSE(drv.isPlaying(sound));
EXPECT_TRUE(drv.isPlaying());
}
}

int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading