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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -430,6 +430,7 @@ namespace e2d
engine::~engine() noexcept {
modules::shutdown<dbgui>();
modules::shutdown<audio>();
modules::shutdown<render>();
modules::shutdown<window>();
modules::shutdown<input>();