diff --git a/headers/enduro2d/core/audio.hpp b/headers/enduro2d/core/audio.hpp index 3885fa7d..c15b5696 100644 --- a/headers/enduro2d/core/audio.hpp +++ b/headers/enduro2d/core/audio.hpp @@ -49,6 +49,7 @@ namespace e2d void play() noexcept; void stop() noexcept; void pause() noexcept; + [[nodiscard]] bool playing() const noexcept; void looping(bool value) noexcept; [[nodiscard]] bool looping() noexcept; @@ -82,19 +83,26 @@ namespace e2d void volume(f32 value) 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); - sound_stream_ptr create_stream( + [[nodiscard]] sound_stream_ptr preload_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); + [[nodiscard]] bool initialized() const noexcept; + void resume() noexcept; void pause() noexcept; - // Applies changes made to the 3D system. + // applies changes made to the 3D system. void apply3d() noexcept; void listener_position(const v3f& value) noexcept; @@ -106,6 +114,8 @@ namespace e2d private: class internal_state; std::unique_ptr state_; + bool initialized_ {false}; + static constexpr u32 max_channels_ = 4; }; } diff --git a/sources/enduro2d/core/audio_impl/audio.hpp b/sources/enduro2d/core/audio_impl/audio.hpp index 1d249a25..76c19ad6 100644 --- a/sources/enduro2d/core/audio_impl/audio.hpp +++ b/sources/enduro2d/core/audio_impl/audio.hpp @@ -10,11 +10,11 @@ # if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS # define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS # 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 # define E2D_AUDIO_MODE E2D_AUDIO_MODE_BASS # 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 diff --git a/sources/enduro2d/core/audio_impl/audio_bass.cpp b/sources/enduro2d/core/audio_impl/audio_bass.cpp index 2704f40b..add87e9c 100644 --- a/sources/enduro2d/core/audio_impl/audio_bass.cpp +++ b/sources/enduro2d/core/audio_impl/audio_bass.cpp @@ -21,6 +21,24 @@ namespace e2d return *state_; } + static void CALLBACK sound_stream_close_proc(void *) { + } + + static QWORD CALLBACK sound_stream_length_proc(void *user) { + auto* self = static_cast(user); + return self->length(); + } + + static DWORD CALLBACK sound_stream_read_proc(void *buffer, DWORD length, void *user) { + auto* self = static_cast(user); + return self->read(buffer, length); + } + + static BOOL CALLBACK sound_stream_seek_proc(QWORD offset, void *user) { + auto* self = static_cast(user); + return self->seek(offset, false); + } + // // sound_source // @@ -47,6 +65,10 @@ namespace e2d 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 { BASS_ChannelFlags(state().channel(), value ? BASS_SAMPLE_LOOP : 0, BASS_SAMPLE_LOOP); } @@ -112,50 +134,99 @@ namespace e2d state_->dbg().error("AUDIO: Initialization failed"); return; } + initialized_ = true; + // uaw one additional thread for streaming + BASS_SetConfig(BASS_CONFIG_UPDATETHREADS, 1); + } audio::~audio() noexcept { BASS_Free(); } - sound_stream_ptr audio::create_stream( + sound_stream_ptr audio::preload_stream( 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"); return nullptr; } - HSAMPLE sample = BASS_SampleLoad(true, sound_data.data(), 0, sound_data.size(), 4, BASS_SAMPLE_OVER_POS ); - if (sample == 0) { + HSAMPLE sample = BASS_SampleLoad(true, sound_data.data(), 0, sound_data.size(), max_channels_, BASS_SAMPLE_OVER_POS ); + if ( sample == 0 ) { state_->dbg().error("AUDIO: Failed to load sound sample, code (%0)", std::to_string(BASS_ErrorGetCode())); return nullptr; } return std::make_shared( - std::make_unique(state_->dbg(), sample)); + std::make_unique(state_->dbg(), sample, nullptr)); } - sound_stream_ptr audio::create_stream( + sound_stream_ptr audio::preload_stream( const input_stream_uptr& file_stream) { + if ( !initialized_ ) { + state_->dbg().error("AUDIO: Not initialized"); + return nullptr; + } 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"); 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( + std::make_unique(state_->dbg(), stream, std::move(file_stream))); } sound_source_ptr audio::create_source( 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"); return nullptr; } - HCHANNEL channel = BASS_SampleGetChannel(stream->state().sound(), false); - if (!channel) { - state_->dbg().error("AUDIO: can net retruve sound channel"); + HCHANNEL channel = 0; + if ( stream->state().stream() ) + 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 std::make_shared( - std::make_unique(state_->dbg(), channel)); + std::make_unique(state_->dbg(), stream, channel)); + } + + bool audio::initialized() const noexcept { + return initialized_; } void audio::volume(f32 value) noexcept { @@ -167,11 +238,19 @@ namespace e2d } void audio::resume() noexcept { + if ( !initialized_ ) { + state_->dbg().error("AUDIO: Not initialized"); + return; + } if ( !BASS_Start() ) state_->dbg().error("AUDIO: Failed to resume audio output"); } void audio::pause() noexcept { + if ( !initialized_ ) { + state_->dbg().error("AUDIO: Not initialized"); + return; + } if ( !BASS_Pause() ) state_->dbg().error("AUDIO: Failed to resume audio output"); } diff --git a/sources/enduro2d/core/audio_impl/audio_bass_impl.cpp b/sources/enduro2d/core/audio_impl/audio_bass_impl.cpp index b42bc077..f1fff89e 100644 --- a/sources/enduro2d/core/audio_impl/audio_bass_impl.cpp +++ b/sources/enduro2d/core/audio_impl/audio_bass_impl.cpp @@ -12,12 +12,21 @@ namespace e2d sound_stream::internal_state::internal_state( debug& debug, - HSAMPLE sound) + DWORD sound, + input_stream_uptr stream) : debug_(debug) - , sound_(sound){ + , sound_(sound) + , stream_(std::move(stream)){ 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 { return debug_; } @@ -26,15 +35,21 @@ namespace e2d return sound_; } + const input_stream_uptr& sound_stream::internal_state::stream() const noexcept { + return stream_; + } + // // sound_source::internal_state // sound_source::internal_state::internal_state( debug& debug, + const sound_stream_ptr& stream, HCHANNEL channel) : debug_(debug) - , channel_(channel) { + , channel_(channel) + , stream_(stream) { E2D_ASSERT(channel); } diff --git a/sources/enduro2d/core/audio_impl/audio_bass_impl.hpp b/sources/enduro2d/core/audio_impl/audio_bass_impl.hpp index 27b2521c..1cf006ae 100644 --- a/sources/enduro2d/core/audio_impl/audio_bass_impl.hpp +++ b/sources/enduro2d/core/audio_impl/audio_bass_impl.hpp @@ -16,14 +16,17 @@ namespace e2d public: internal_state( debug& debug, - HSAMPLE sound); - ~internal_state() noexcept = default; + DWORD sound, + input_stream_uptr stream); + ~internal_state() noexcept; public: - debug& dbg() const noexcept; - HSAMPLE sound() const noexcept; + [[nodiscard]] debug& dbg() const noexcept; + [[nodiscard]] DWORD sound() const noexcept; + [[nodiscard]] const input_stream_uptr& stream() const noexcept; private: debug& debug_; - HSAMPLE sound_; + DWORD sound_; + input_stream_uptr stream_; }; // @@ -34,14 +37,16 @@ namespace e2d public: internal_state( debug& debug, + const sound_stream_ptr& stream, HCHANNEL channel); ~internal_state() noexcept = default; public: - debug& dbg() const noexcept; - HCHANNEL channel() const noexcept; + [[nodiscard]] debug& dbg() const noexcept; + [[nodiscard]] HCHANNEL channel() const noexcept; private: debug& debug_; HCHANNEL channel_; + sound_stream_ptr stream_; }; // @@ -53,7 +58,7 @@ namespace e2d internal_state(debug& debug); ~internal_state() noexcept = default; public: - debug& dbg() const noexcept; + [[nodiscard]] debug& dbg() const noexcept; private: debug& debug_; }; diff --git a/sources/enduro2d/core/audio_impl/audio_none.cpp b/sources/enduro2d/core/audio_impl/audio_none.cpp index a22164f0..50b8394f 100644 --- a/sources/enduro2d/core/audio_impl/audio_none.cpp +++ b/sources/enduro2d/core/audio_impl/audio_none.cpp @@ -123,27 +123,37 @@ namespace e2d 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) { E2D_UNUSED(sound_data); return nullptr; } - sound_stream_ptr audio::create_stream( + sound_stream_ptr audio::preload_stream( const input_stream_uptr& file_stream) { E2D_UNUSED(file_stream); 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( const sound_stream_ptr &stream) { E2D_UNUSED(stream); return nullptr; } + bool audio::initialized() const noexcept { + return false; + } + void audio::volume(f32 value) noexcept { E2D_UNUSED(value); } diff --git a/sources/enduro2d/core/engine.cpp b/sources/enduro2d/core/engine.cpp index f7c11415..bc6ab0cd 100644 --- a/sources/enduro2d/core/engine.cpp +++ b/sources/enduro2d/core/engine.cpp @@ -430,6 +430,7 @@ namespace e2d engine::~engine() noexcept { modules::shutdown(); + modules::shutdown