mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-14 16:09:06 +07:00
added sound streaming
This commit is contained in:
@@ -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<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
|
||||
# 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
|
||||
|
||||
|
||||
@@ -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<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
|
||||
//
|
||||
@@ -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<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) {
|
||||
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<sound_stream>(
|
||||
std::make_unique<sound_stream::internal_state>(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<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 {
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -430,6 +430,7 @@ namespace e2d
|
||||
|
||||
engine::~engine() noexcept {
|
||||
modules::shutdown<dbgui>();
|
||||
modules::shutdown<audio>();
|
||||
modules::shutdown<render>();
|
||||
modules::shutdown<window>();
|
||||
modules::shutdown<input>();
|
||||
|
||||
Reference in New Issue
Block a user