added sound streaming

This commit is contained in:
andrey.zhirnov
2019-06-07 14:38:03 +03:00
parent 7aad845b50
commit d27ebc6d7c
7 changed files with 153 additions and 33 deletions

View File

@@ -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;
}; };
} }

View File

@@ -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

View File

@@ -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");
} }

View File

@@ -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);
} }

View File

@@ -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_;
}; };

View File

@@ -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);
} }

View File

@@ -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>();