mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 08:15:38 +07:00
added sound streaming
This commit is contained in:
@@ -49,6 +49,7 @@ namespace e2d
|
|||||||
void play() noexcept;
|
void play() noexcept;
|
||||||
void stop() noexcept;
|
void stop() noexcept;
|
||||||
void pause() noexcept;
|
void pause() noexcept;
|
||||||
|
[[nodiscard]] bool playing() const noexcept;
|
||||||
|
|
||||||
void looping(bool value) noexcept;
|
void looping(bool value) noexcept;
|
||||||
[[nodiscard]] bool looping() noexcept;
|
[[nodiscard]] bool looping() noexcept;
|
||||||
@@ -82,19 +83,26 @@ namespace e2d
|
|||||||
void volume(f32 value) noexcept;
|
void volume(f32 value) noexcept;
|
||||||
[[nodiscard]] f32 volume() const noexcept;
|
[[nodiscard]] f32 volume() const noexcept;
|
||||||
|
|
||||||
sound_stream_ptr create_stream(
|
// preloaded sound supports multiple sources
|
||||||
|
[[nodiscard]] sound_stream_ptr preload_stream(
|
||||||
buffer_view sound_data);
|
buffer_view sound_data);
|
||||||
|
|
||||||
sound_stream_ptr create_stream(
|
[[nodiscard]] sound_stream_ptr preload_stream(
|
||||||
const input_stream_uptr& file_stream);
|
const input_stream_uptr& file_stream);
|
||||||
|
|
||||||
sound_source_ptr create_source(
|
// stream supports only one source
|
||||||
|
[[nodiscard]] sound_stream_ptr create_stream(
|
||||||
|
input_stream_uptr file_stream);
|
||||||
|
|
||||||
|
[[nodiscard]] sound_source_ptr create_source(
|
||||||
const sound_stream_ptr &stream);
|
const sound_stream_ptr &stream);
|
||||||
|
|
||||||
|
[[nodiscard]] bool initialized() const noexcept;
|
||||||
|
|
||||||
void resume() noexcept;
|
void resume() noexcept;
|
||||||
void pause() noexcept;
|
void pause() noexcept;
|
||||||
|
|
||||||
// Applies changes made to the 3D system.
|
// applies changes made to the 3D system.
|
||||||
void apply3d() noexcept;
|
void apply3d() noexcept;
|
||||||
|
|
||||||
void listener_position(const v3f& value) noexcept;
|
void listener_position(const v3f& value) noexcept;
|
||||||
@@ -106,6 +114,8 @@ namespace e2d
|
|||||||
private:
|
private:
|
||||||
class internal_state;
|
class internal_state;
|
||||||
std::unique_ptr<internal_state> state_;
|
std::unique_ptr<internal_state> state_;
|
||||||
|
bool initialized_ {false};
|
||||||
|
static constexpr u32 max_channels_ = 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
# if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
|
# if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
|
||||||
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
||||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX
|
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX
|
||||||
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_NONE
|
||||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX
|
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX
|
||||||
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
||||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS
|
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS
|
||||||
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS
|
# define E2D_AUDIO_MODE E2D_AUDIO_MODE_NONE
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,24 @@ namespace e2d
|
|||||||
return *state_;
|
return *state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void CALLBACK sound_stream_close_proc(void *) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static QWORD CALLBACK sound_stream_length_proc(void *user) {
|
||||||
|
auto* self = static_cast<input_stream*>(user);
|
||||||
|
return self->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD CALLBACK sound_stream_read_proc(void *buffer, DWORD length, void *user) {
|
||||||
|
auto* self = static_cast<input_stream*>(user);
|
||||||
|
return self->read(buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL CALLBACK sound_stream_seek_proc(QWORD offset, void *user) {
|
||||||
|
auto* self = static_cast<input_stream*>(user);
|
||||||
|
return self->seek(offset, false);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// sound_source
|
// sound_source
|
||||||
//
|
//
|
||||||
@@ -47,6 +65,10 @@ namespace e2d
|
|||||||
BASS_ChannelPause(state().channel());
|
BASS_ChannelPause(state().channel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sound_source::playing() const noexcept {
|
||||||
|
return BASS_ChannelIsActive(state().channel()) == BASS_ACTIVE_PLAYING;
|
||||||
|
}
|
||||||
|
|
||||||
void sound_source::looping(bool value) noexcept {
|
void sound_source::looping(bool value) noexcept {
|
||||||
BASS_ChannelFlags(state().channel(), value ? BASS_SAMPLE_LOOP : 0, BASS_SAMPLE_LOOP);
|
BASS_ChannelFlags(state().channel(), value ? BASS_SAMPLE_LOOP : 0, BASS_SAMPLE_LOOP);
|
||||||
}
|
}
|
||||||
@@ -112,50 +134,99 @@ namespace e2d
|
|||||||
state_->dbg().error("AUDIO: Initialization failed");
|
state_->dbg().error("AUDIO: Initialization failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
initialized_ = true;
|
||||||
|
// uaw one additional thread for streaming
|
||||||
|
BASS_SetConfig(BASS_CONFIG_UPDATETHREADS, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
audio::~audio() noexcept {
|
audio::~audio() noexcept {
|
||||||
BASS_Free();
|
BASS_Free();
|
||||||
}
|
}
|
||||||
|
|
||||||
sound_stream_ptr audio::create_stream(
|
sound_stream_ptr audio::preload_stream(
|
||||||
buffer_view sound_data) {
|
buffer_view sound_data) {
|
||||||
if (sound_data.empty()) {
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if ( sound_data.empty() ) {
|
||||||
state_->dbg().error("AUDIO: Sound data is empty");
|
state_->dbg().error("AUDIO: Sound data is empty");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HSAMPLE sample = BASS_SampleLoad(true, sound_data.data(), 0, sound_data.size(), 4, BASS_SAMPLE_OVER_POS );
|
HSAMPLE sample = BASS_SampleLoad(true, sound_data.data(), 0, sound_data.size(), max_channels_, BASS_SAMPLE_OVER_POS );
|
||||||
if (sample == 0) {
|
if ( sample == 0 ) {
|
||||||
state_->dbg().error("AUDIO: Failed to load sound sample, code (%0)", std::to_string(BASS_ErrorGetCode()));
|
state_->dbg().error("AUDIO: Failed to load sound sample, code (%0)", std::to_string(BASS_ErrorGetCode()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::make_shared<sound_stream>(
|
return std::make_shared<sound_stream>(
|
||||||
std::make_unique<sound_stream::internal_state>(state_->dbg(), sample));
|
std::make_unique<sound_stream::internal_state>(state_->dbg(), sample, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
sound_stream_ptr audio::create_stream(
|
sound_stream_ptr audio::preload_stream(
|
||||||
const input_stream_uptr& file_stream) {
|
const input_stream_uptr& file_stream) {
|
||||||
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
buffer file_data;
|
buffer file_data;
|
||||||
if (!streams::try_read_tail(file_data, file_stream)) {
|
if ( !streams::try_read_tail(file_data, file_stream) ) {
|
||||||
state_->dbg().error("AUDIO: Failed to read file");
|
state_->dbg().error("AUDIO: Failed to read file");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return create_stream(buffer_view(file_data));
|
return preload_stream(buffer_view(file_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
sound_stream_ptr audio::create_stream(
|
||||||
|
input_stream_uptr file_stream) {
|
||||||
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if ( !file_stream ) {
|
||||||
|
state_->dbg().error("AUDIO: file stream is null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
BASS_FILEPROCS procs;
|
||||||
|
procs.close = &sound_stream_close_proc;
|
||||||
|
procs.length = &sound_stream_length_proc;
|
||||||
|
procs.read = &sound_stream_read_proc;
|
||||||
|
procs.seek = &sound_stream_seek_proc;
|
||||||
|
HSTREAM stream = BASS_StreamCreateFileUser(STREAMFILE_NOBUFFER, 0, &procs, file_stream.get());
|
||||||
|
if ( stream == 0 ) {
|
||||||
|
state_->dbg().error("AUDIO: Failed to create sound stream, code (%0)", std::to_string(BASS_ErrorGetCode()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_shared<sound_stream>(
|
||||||
|
std::make_unique<sound_stream::internal_state>(state_->dbg(), stream, std::move(file_stream)));
|
||||||
}
|
}
|
||||||
|
|
||||||
sound_source_ptr audio::create_source(
|
sound_source_ptr audio::create_source(
|
||||||
const sound_stream_ptr &stream) {
|
const sound_stream_ptr &stream) {
|
||||||
if (!stream) {
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if ( !stream ) {
|
||||||
state_->dbg().error("AUDIO: stream is null");
|
state_->dbg().error("AUDIO: stream is null");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HCHANNEL channel = BASS_SampleGetChannel(stream->state().sound(), false);
|
HCHANNEL channel = 0;
|
||||||
if (!channel) {
|
if ( stream->state().stream() )
|
||||||
state_->dbg().error("AUDIO: can net retruve sound channel");
|
channel = stream->state().sound();
|
||||||
|
else
|
||||||
|
channel = BASS_SampleGetChannel(stream->state().sound(), false);
|
||||||
|
|
||||||
|
if ( !channel ) {
|
||||||
|
state_->dbg().error("AUDIO: can net retrive sound channel");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::make_shared<sound_source>(
|
return std::make_shared<sound_source>(
|
||||||
std::make_unique<sound_source::internal_state>(state_->dbg(), channel));
|
std::make_unique<sound_source::internal_state>(state_->dbg(), stream, channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool audio::initialized() const noexcept {
|
||||||
|
return initialized_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::volume(f32 value) noexcept {
|
void audio::volume(f32 value) noexcept {
|
||||||
@@ -167,11 +238,19 @@ namespace e2d
|
|||||||
}
|
}
|
||||||
|
|
||||||
void audio::resume() noexcept {
|
void audio::resume() noexcept {
|
||||||
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ( !BASS_Start() )
|
if ( !BASS_Start() )
|
||||||
state_->dbg().error("AUDIO: Failed to resume audio output");
|
state_->dbg().error("AUDIO: Failed to resume audio output");
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::pause() noexcept {
|
void audio::pause() noexcept {
|
||||||
|
if ( !initialized_ ) {
|
||||||
|
state_->dbg().error("AUDIO: Not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ( !BASS_Pause() )
|
if ( !BASS_Pause() )
|
||||||
state_->dbg().error("AUDIO: Failed to resume audio output");
|
state_->dbg().error("AUDIO: Failed to resume audio output");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,21 @@ namespace e2d
|
|||||||
|
|
||||||
sound_stream::internal_state::internal_state(
|
sound_stream::internal_state::internal_state(
|
||||||
debug& debug,
|
debug& debug,
|
||||||
HSAMPLE sound)
|
DWORD sound,
|
||||||
|
input_stream_uptr stream)
|
||||||
: debug_(debug)
|
: debug_(debug)
|
||||||
, sound_(sound){
|
, sound_(sound)
|
||||||
|
, stream_(std::move(stream)){
|
||||||
E2D_ASSERT(sound);
|
E2D_ASSERT(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sound_stream::internal_state::~internal_state() noexcept {
|
||||||
|
if ( stream_ )
|
||||||
|
BASS_StreamFree(sound_);
|
||||||
|
else
|
||||||
|
BASS_SampleFree(sound_);
|
||||||
|
}
|
||||||
|
|
||||||
debug& sound_stream::internal_state::dbg() const noexcept {
|
debug& sound_stream::internal_state::dbg() const noexcept {
|
||||||
return debug_;
|
return debug_;
|
||||||
}
|
}
|
||||||
@@ -26,15 +35,21 @@ namespace e2d
|
|||||||
return sound_;
|
return sound_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const input_stream_uptr& sound_stream::internal_state::stream() const noexcept {
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// sound_source::internal_state
|
// sound_source::internal_state
|
||||||
//
|
//
|
||||||
|
|
||||||
sound_source::internal_state::internal_state(
|
sound_source::internal_state::internal_state(
|
||||||
debug& debug,
|
debug& debug,
|
||||||
|
const sound_stream_ptr& stream,
|
||||||
HCHANNEL channel)
|
HCHANNEL channel)
|
||||||
: debug_(debug)
|
: debug_(debug)
|
||||||
, channel_(channel) {
|
, channel_(channel)
|
||||||
|
, stream_(stream) {
|
||||||
E2D_ASSERT(channel);
|
E2D_ASSERT(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,17 @@ namespace e2d
|
|||||||
public:
|
public:
|
||||||
internal_state(
|
internal_state(
|
||||||
debug& debug,
|
debug& debug,
|
||||||
HSAMPLE sound);
|
DWORD sound,
|
||||||
~internal_state() noexcept = default;
|
input_stream_uptr stream);
|
||||||
|
~internal_state() noexcept;
|
||||||
public:
|
public:
|
||||||
debug& dbg() const noexcept;
|
[[nodiscard]] debug& dbg() const noexcept;
|
||||||
HSAMPLE sound() const noexcept;
|
[[nodiscard]] DWORD sound() const noexcept;
|
||||||
|
[[nodiscard]] const input_stream_uptr& stream() const noexcept;
|
||||||
private:
|
private:
|
||||||
debug& debug_;
|
debug& debug_;
|
||||||
HSAMPLE sound_;
|
DWORD sound_;
|
||||||
|
input_stream_uptr stream_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -34,14 +37,16 @@ namespace e2d
|
|||||||
public:
|
public:
|
||||||
internal_state(
|
internal_state(
|
||||||
debug& debug,
|
debug& debug,
|
||||||
|
const sound_stream_ptr& stream,
|
||||||
HCHANNEL channel);
|
HCHANNEL channel);
|
||||||
~internal_state() noexcept = default;
|
~internal_state() noexcept = default;
|
||||||
public:
|
public:
|
||||||
debug& dbg() const noexcept;
|
[[nodiscard]] debug& dbg() const noexcept;
|
||||||
HCHANNEL channel() const noexcept;
|
[[nodiscard]] HCHANNEL channel() const noexcept;
|
||||||
private:
|
private:
|
||||||
debug& debug_;
|
debug& debug_;
|
||||||
HCHANNEL channel_;
|
HCHANNEL channel_;
|
||||||
|
sound_stream_ptr stream_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -53,7 +58,7 @@ namespace e2d
|
|||||||
internal_state(debug& debug);
|
internal_state(debug& debug);
|
||||||
~internal_state() noexcept = default;
|
~internal_state() noexcept = default;
|
||||||
public:
|
public:
|
||||||
debug& dbg() const noexcept;
|
[[nodiscard]] debug& dbg() const noexcept;
|
||||||
private:
|
private:
|
||||||
debug& debug_;
|
debug& debug_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -123,27 +123,37 @@ namespace e2d
|
|||||||
E2D_UNUSED(d);
|
E2D_UNUSED(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
audio::audio() noexcept {
|
audio::~audio() noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
sound_stream_ptr audio::create_stream(
|
sound_stream_ptr audio::preload_stream(
|
||||||
buffer_view sound_data) {
|
buffer_view sound_data) {
|
||||||
E2D_UNUSED(sound_data);
|
E2D_UNUSED(sound_data);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound_stream_ptr audio::create_stream(
|
sound_stream_ptr audio::preload_stream(
|
||||||
const input_stream_uptr& file_stream) {
|
const input_stream_uptr& file_stream) {
|
||||||
E2D_UNUSED(file_stream);
|
E2D_UNUSED(file_stream);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sound_stream_ptr audio::create_stream(
|
||||||
|
input_stream_uptr file_stream) {
|
||||||
|
E2D_UNUSED(file_stream);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
sound_source_ptr audio::create_source(
|
sound_source_ptr audio::create_source(
|
||||||
const sound_stream_ptr &stream) {
|
const sound_stream_ptr &stream) {
|
||||||
E2D_UNUSED(stream);
|
E2D_UNUSED(stream);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool audio::initialized() const noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void audio::volume(f32 value) noexcept {
|
void audio::volume(f32 value) noexcept {
|
||||||
E2D_UNUSED(value);
|
E2D_UNUSED(value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ namespace e2d
|
|||||||
|
|
||||||
engine::~engine() noexcept {
|
engine::~engine() noexcept {
|
||||||
modules::shutdown<dbgui>();
|
modules::shutdown<dbgui>();
|
||||||
|
modules::shutdown<audio>();
|
||||||
modules::shutdown<render>();
|
modules::shutdown<render>();
|
||||||
modules::shutdown<window>();
|
modules::shutdown<window>();
|
||||||
modules::shutdown<input>();
|
modules::shutdown<input>();
|
||||||
|
|||||||
Reference in New Issue
Block a user