From 6788eb0c2bf29b155aa7a0306118a7efa038f0d6 Mon Sep 17 00:00:00 2001 From: mrkubax10 Date: Thu, 4 Jan 2024 22:37:28 +0100 Subject: [PATCH] Refactor AmbientSound (#2688) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements new SoundObject object that is hearable in entire Sector. It is possible to change it's volume and looping sound play interval. Attempts to fix the long-broken AmbientSound. Co-authored-by: Rusty-Box 8927 bytes data/images/engine/editor/objects.stoi | 9 +- data/images/engine/editor/sound.png | Bin 0 -> 9258 bytes src/audio/dummy_sound_source.cpp | 2 +- src/audio/openal_sound_source.cpp | 5 +- src/audio/openal_sound_source.hpp | 2 +- src/audio/sound_source.hpp | 2 +- src/object/ambient_sound.cpp | 255 ++++++++++--------------- src/object/ambient_sound.hpp | 47 ++--- src/object/sound_object.cpp | 124 ++++++++++++ src/object/sound_object.hpp | 72 +++++++ src/scripting/sound_object.cpp | 53 +++++ src/scripting/sound_object.hpp | 74 +++++++ src/scripting/wrapper.cpp | 178 +++++++++++++++++ src/scripting/wrapper.hpp | 2 + src/scripting/wrapper.interface.hpp | 1 + src/supertux/game_object_factory.cpp | 2 + 17 files changed, 625 insertions(+), 203 deletions(-) create mode 100644 data/images/engine/editor/sound.png create mode 100644 src/object/sound_object.cpp create mode 100644 src/object/sound_object.hpp create mode 100644 src/scripting/sound_object.cpp create mode 100644 src/scripting/sound_object.hpp diff --git a/data/images/engine/editor/music.png b/data/images/engine/editor/music.png index 8f2172f36ec29304120e801fa0e10eadae1ed182..eeca404198e4a10c7eeb35847ad0345d8e50d052 100644 GIT binary patch delta 5092 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

n{L_t(o!^M|t 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 literal 0 HcmV?d00001 diff --git a/src/audio/dummy_sound_source.cpp b/src/audio/dummy_sound_source.cpp index 65d2c755cf7..92fc06020a0 100644 --- a/src/audio/dummy_sound_source.cpp +++ b/src/audio/dummy_sound_source.cpp @@ -34,7 +34,7 @@ class DummySoundSource final : public SoundSource is_playing = true; } - virtual void stop() override + virtual void stop(bool) override { is_playing = false; } diff --git a/src/audio/openal_sound_source.cpp b/src/audio/openal_sound_source.cpp index 6ad5a03454a..f009a6baef0 100644 --- a/src/audio/openal_sound_source.cpp +++ b/src/audio/openal_sound_source.cpp @@ -40,7 +40,7 @@ OpenALSoundSource::~OpenALSoundSource() } void -OpenALSoundSource::stop() +OpenALSoundSource::stop(bool unload_buffer) { #ifdef WIN32 // See commit 417a8e7a8c599bfc2dceaec7b6f64ac865318ef1 @@ -48,7 +48,8 @@ OpenALSoundSource::stop() #else alSourceStop(m_source); #endif - alSourcei(m_source, AL_BUFFER, AL_NONE); + if (unload_buffer) + alSourcei(m_source, AL_BUFFER, AL_NONE); try { SoundManager::check_al_error("Problem stopping audio source: "); diff --git a/src/audio/openal_sound_source.hpp b/src/audio/openal_sound_source.hpp index 50769a47e15..c0649c10a36 100644 --- a/src/audio/openal_sound_source.hpp +++ b/src/audio/openal_sound_source.hpp @@ -30,7 +30,7 @@ class OpenALSoundSource : public SoundSource ~OpenALSoundSource() override; virtual void play() override; - virtual void stop() override; + virtual void stop(bool unload_buffer = true) override; virtual bool playing() const override; virtual void set_looping(bool looping) override; diff --git a/src/audio/sound_source.hpp b/src/audio/sound_source.hpp index 50bc40bbaa5..d1ccde14133 100644 --- a/src/audio/sound_source.hpp +++ b/src/audio/sound_source.hpp @@ -29,7 +29,7 @@ class SoundSource virtual ~SoundSource() {} virtual void play() = 0; - virtual void stop() = 0; + virtual void stop(bool unload_buffer = true) = 0; virtual bool playing() const = 0; virtual void set_looping(bool looping) = 0; diff --git a/src/object/ambient_sound.cpp b/src/object/ambient_sound.cpp index dc40c9533e3..9e6e4eb0427 100644 --- a/src/object/ambient_sound.cpp +++ b/src/object/ambient_sound.cpp @@ -1,5 +1,6 @@ // SuperTux // Copyright (C) 2006 Matthias Braun +// 2023 mrkubax10 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,7 +22,7 @@ #include "audio/sound_manager.hpp" #include "audio/sound_source.hpp" #include "editor/editor.hpp" -#include "object/camera.hpp" +#include "object/player.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" #include "video/drawing_context.hpp" @@ -29,15 +30,12 @@ AmbientSound::AmbientSound(const ReaderMapping& mapping) : MovingObject(mapping), ExposedObject(this), - sample(), - sound_source(), - latency(), - distance_factor(), - distance_bias(), - silence_distance(), - maximumvolume(), - targetvolume(), - currentvolume(0) + m_sample(), + m_sound_source(), + m_radius(), + m_radius_in_px(), + m_volume(), + m_has_played_sound(false) { m_col.m_group = COLGROUP_DISABLED; @@ -48,67 +46,35 @@ AmbientSound::AmbientSound(const ReaderMapping& mapping) : mapping.get("height", h, 32.0f); m_col.m_bbox.set_size(w, h); - mapping.get("distance_factor",distance_factor, 0.0f); - mapping.get("distance_bias" ,distance_bias , 0.0f); - mapping.get("sample" ,sample , ""); - mapping.get("volume" ,maximumvolume , 1.0f); + mapping.get("radius", m_radius, 1.0f); + mapping.get("sample", m_sample, ""); + mapping.get("volume", m_volume, 1.0f); - // Square all distances (saves us a sqrt later). + m_radius_in_px = m_radius*32.0f; - if (!Editor::is_active()) { - distance_bias*=distance_bias; - distance_factor*=distance_factor; - } - - // Set default silence_distance. - - if (distance_factor == 0) - silence_distance = std::numeric_limits::max(); - else - silence_distance = 1/distance_factor; - - mapping.get("silence_distance",silence_distance); - - if (!Editor::is_active()) { - sound_source.reset(); // Resetting the sound source to stop playing at the beginning. - SoundManager::current()->preload(sample); - } - latency=0; + prepare_sound_source(); } -AmbientSound::AmbientSound(const Vector& pos, float factor, float bias, float vol, const std::string& file) : +AmbientSound::AmbientSound(const Vector& pos, float radius, float vol, const std::string& file) : ExposedObject(this), - sample(file), - sound_source(), - latency(0), - distance_factor(factor * factor), - distance_bias(bias * bias), - silence_distance(), - maximumvolume(vol), - targetvolume(), - currentvolume() + m_sample(file), + m_sound_source(), + m_radius(radius), + m_radius_in_px(m_radius*32.0f), + m_volume(vol), + m_has_played_sound(false) { m_col.m_group = COLGROUP_DISABLED; m_col.m_bbox.set_pos(pos); m_col.m_bbox.set_size(32, 32); - // Set default silence_distance. - - if (distance_factor == 0) - silence_distance = std::numeric_limits::max(); - else - silence_distance = 1/distance_factor; - - if (!Editor::is_active()) { - sound_source.reset(); // Resetting the sound source to stop playing at the beginning. - SoundManager::current()->preload(sample); - } + prepare_sound_source(); } AmbientSound::~AmbientSound() { - stop_playing(); + stop_looping_sounds(); } ObjectSettings @@ -116,107 +82,15 @@ AmbientSound::get_settings() { ObjectSettings result = MovingObject::get_settings(); - result.add_sound(_("Sound"), &sample, "sample"); - result.add_float(_("Distance factor"), &distance_factor, "distance_factor"); - result.add_float(_("Distance bias"), &distance_bias, "distance_bias"); - result.add_float(_("Volume"), &maximumvolume, "volume"); + result.add_sound(_("Sound"), &m_sample, "sample"); + result.add_float(_("Radius (in tiles)"), &m_radius, "radius"); + result.add_float(_("Volume"), &m_volume, "volume"); - result.reorder({"sample", "distance_factor", "distance_bias", "volume", "region", "name", "x", "y", "width", "height"}); + result.reorder({"sample", "radius", "volume", "region", "name", "x", "y", "width", "height"}); return result; } -void -AmbientSound::after_editor_set() -{ -} - -void -AmbientSound::stop_playing() -{ - sound_source.reset(); -} - -void -AmbientSound::start_playing() -{ - if (Editor::is_active()) return; - - try { - sound_source = SoundManager::current()->create_sound_source(sample); - if (!sound_source) - throw std::runtime_error("file not found"); - - sound_source->set_gain(0); - sound_source->set_looping(true); - sound_source->set_position(m_col.m_bbox.get_middle()); - currentvolume=targetvolume=1e-20f; - sound_source->play(); - } catch(std::exception& e) { - log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl; - sound_source.reset(); - remove_me(); - } -} - -void -AmbientSound::update(float dt_sec) -{ - if (latency-- <= 0) { - float px,py; - float rx,ry; - - // Get the central position of the camera. - px=Sector::get().get_camera().get_center().x; - py=Sector::get().get_camera().get_center().y; - - // Determine the nearest point within the area bounds. - rx=pxset_gain(currentvolume*maximumvolume); - - if (sqrdistance>=silence_distance && currentvolume < 1e-3f) - stop_playing(); - latency=0; - } else { - if (sqrdistance(0.001f / distance_factor); - //(int)(10*((sqrdistance-silence_distance)/silence_distance)); - } - } - - // Heuristically measured "good" maximum latency. - - // if (latency > 0.001 / distance_factor) - // latency = -} - #ifndef SCRIPTING_API void AmbientSound::set_pos(const Vector& pos) @@ -252,22 +126,91 @@ AmbientSound::collision(GameObject& other, const CollisionHit& hit_) void AmbientSound::draw(DrawingContext& context) { - if (Editor::is_active()) { + if (Editor::is_active()) context.color().draw_filled_rect(m_col.m_bbox, Color(0.0f, 0.0f, 1.0f, 0.6f), 0.0f, LAYER_OBJECTS); - } } void AmbientSound::stop_looping_sounds() { - stop_playing(); + if (m_sound_source) + m_sound_source->stop(false); } void AmbientSound::play_looping_sounds() { - start_playing(); + if (Editor::is_active()) + return; + + m_sound_source->play(); +} + +void +AmbientSound::update(float dt_sec) +{ + const Player* const nearest_player = Sector::get().get_nearest_player(get_bbox().get_middle()); + if (!nearest_player) + return; + const Rectf& player_bbox = nearest_player->get_bbox(); + const Vector player_center = player_bbox.get_middle(); + + if (get_bbox().contains(player_bbox)) + m_sound_source->set_gain(m_volume); + else + { + float player_distance = m_radius+1; + if (player_center.x >= get_bbox().get_left() && player_center.x <= get_bbox().get_right()) + player_distance = player_center.y < get_bbox().get_top() ? get_bbox().get_top() - player_center.y : player_center.y - get_bbox().get_bottom(); + else if (player_center.y >= get_bbox().get_top() && player_center.y <= get_bbox().get_bottom()) + player_distance = player_center.x < get_bbox().get_left() ? get_bbox().get_left() - player_center.x : player_center.x - get_bbox().get_right(); + else if (player_center.x <= get_bbox().get_left() && player_center.y <= get_bbox().get_top()) + player_distance = glm::distance(player_center, get_bbox().p1()); + else if (player_center.x >= get_bbox().get_right() && player_center.y <= get_bbox().get_top()) + player_distance = glm::distance(player_center, get_bbox().p1() + Vector(get_bbox().get_width(), 0)); + else if (player_center.x <= get_bbox().get_left() && player_center.y >= get_bbox().get_bottom()) + player_distance = glm::distance(player_center, get_bbox().p1() + Vector(0, get_bbox().get_height())); + else if (player_center.x >= get_bbox().get_right() && player_center.y >= get_bbox().get_bottom()) + player_distance = glm::distance(player_center, get_bbox().p2()); + m_sound_source->set_gain(std::max(m_radius_in_px - player_distance, 0.0f) / m_radius_in_px * m_volume); + } + + if (!m_has_played_sound) + { + m_sound_source->play(); + m_has_played_sound = true; + } +} + +void +AmbientSound::prepare_sound_source() +{ + if (Editor::is_active()) + return; + + if (m_sample.empty()) + { + remove_me(); + return; + } + + try + { + m_sound_source = SoundManager::current()->create_sound_source(m_sample); + if (!m_sound_source) + throw std::runtime_error("file not found"); + + m_sound_source->set_gain(0); + m_sound_source->set_looping(true); + m_sound_source->set_relative(true); + } + catch(const std::exception& e) + { + log_warning << "Couldn't load '" << m_sample << "': " << e.what() << std::endl; + m_sound_source.reset(); + remove_me(); + } } /* EOF */ diff --git a/src/object/ambient_sound.hpp b/src/object/ambient_sound.hpp index 7e94aceb81f..2bf0994f10d 100644 --- a/src/object/ambient_sound.hpp +++ b/src/object/ambient_sound.hpp @@ -1,5 +1,6 @@ // SuperTux // Copyright (C) 2006 Matthias Braun +// 2023 mrkubax10 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,28 +15,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -/** - * Ambient Sound Source, gamma version. Features: - * - * - "rounded rectangle" geometry with position, dimension and - * "rounding radius" (extending in all directions) of a 100% - * volume area, adjustable maximum volume, inverse square - * falloff outside area. - * - * - degenerates gracefully to a disc for dimension=0 - * - * - parameters: - * - * x, y position - * width, height dimension - * distance_factor high = steep falloff - * distance_bias high = big "100% disc" - * silence_distance defaults reasonably. - * sample sample to be played back in loop mode - * - * basti_ - */ - #ifndef HEADER_SUPERTUX_OBJECT_AMBIENT_SOUND_HPP #define HEADER_SUPERTUX_OBJECT_AMBIENT_SOUND_HPP @@ -50,11 +29,11 @@ class ReaderMapping; class SoundSource; class AmbientSound final : public MovingObject, - public ExposedObject + public ExposedObject { public: AmbientSound(const ReaderMapping& mapping); - AmbientSound(const Vector& pos, float factor, float bias, float vol, const std::string& file); + AmbientSound(const Vector& pos, float radius, float vol, const std::string& file); ~AmbientSound() override; virtual HitResponse collision(GameObject& other, const CollisionHit& hit_) override; @@ -78,7 +57,6 @@ class AmbientSound final : public MovingObject, virtual void draw(DrawingContext& context) override; virtual ObjectSettings get_settings() override; - virtual void after_editor_set() override; virtual int get_layer() const override { return LAYER_OBJECTS; } @@ -87,21 +65,18 @@ class AmbientSound final : public MovingObject, protected: virtual void update(float dt_sec) override; - virtual void start_playing(); - virtual void stop_playing(); private: - std::string sample; - std::unique_ptr sound_source; - int latency; + void prepare_sound_source(); - float distance_factor; /// distance scaling - float distance_bias; /// 100% volume disc radius - float silence_distance; /// not implemented yet +private: + std::string m_sample; + std::unique_ptr m_sound_source; - float maximumvolume; /// maximum volume - float targetvolume; /// how loud we want to be - float currentvolume; /// how loud we are + float m_radius; + float m_radius_in_px; + float m_volume; + bool m_has_played_sound; private: AmbientSound(const AmbientSound&) = delete; diff --git a/src/object/sound_object.cpp b/src/object/sound_object.cpp new file mode 100644 index 00000000000..7b54fc85939 --- /dev/null +++ b/src/object/sound_object.cpp @@ -0,0 +1,124 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "object/sound_object.hpp" + +#include + +#include "audio/sound_manager.hpp" +#include "audio/sound_source.hpp" +#include "editor/editor.hpp" +#include "util/reader_mapping.hpp" + +SoundObject::SoundObject(const ReaderMapping& mapping) : + GameObject(mapping), + ExposedObject(this), + m_sample(), + m_sound_source(), + m_volume() +{ + mapping.get("sample", m_sample, ""); + mapping.get("volume", m_volume, 1.0f); + + prepare_sound_source(); +} + +SoundObject::SoundObject(float vol, const std::string& file) : + ExposedObject(this), + m_sample(file), + m_sound_source(), + m_volume(vol) +{ + prepare_sound_source(); +} + +SoundObject::~SoundObject() +{ + stop_looping_sounds(); +} + +ObjectSettings +SoundObject::get_settings() +{ + ObjectSettings result = GameObject::get_settings(); + + result.add_sound(_("Sound"), &m_sample, "sample"); + result.add_float(_("Volume"), &m_volume, "volume"); + + result.reorder({"sample", "volume", "name"}); + + result.add_remove(); + + return result; +} + +void +SoundObject::stop_looping_sounds() +{ + if (m_sound_source) + m_sound_source->stop(false); +} + +void +SoundObject::play_looping_sounds() +{ + if (!Editor::is_active() && m_sound_source) + m_sound_source->play(); +} + +void +SoundObject::prepare_sound_source() +{ + if (Editor::is_active()) + return; + + if (m_sample.empty()) + { + remove_me(); + return; + } + + try + { + m_sound_source = SoundManager::current()->create_sound_source(m_sample); + if (!m_sound_source) + throw std::runtime_error("file not found"); + + // Maybe FIXME: Apparently this is an OpenAL error, if gain is not set to 0 before making source looping + // it won't be possible to pause it. + m_sound_source->set_gain(0); + m_sound_source->set_looping(true); + m_sound_source->set_relative(true); + m_sound_source->set_gain(m_volume); + + m_sound_source->play(); + } + catch(const std::exception& e) + { + log_warning << "Couldn't load '" << m_sample << "': " << e.what() << std::endl; + m_sound_source.reset(); + remove_me(); + } +} + +void +SoundObject::set_volume(float volume) +{ + m_volume = volume; + m_sound_source->set_gain(volume); +} + +/* EOF */ diff --git a/src/object/sound_object.hpp b/src/object/sound_object.hpp new file mode 100644 index 00000000000..9d6bf8c1e2c --- /dev/null +++ b/src/object/sound_object.hpp @@ -0,0 +1,72 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HEADER_SUPERTUX_SOUND_OBJECT_HPP +#define HEADER_SUPERTUX_SOUND_OBJECT_HPP + +#include "supertux/game_object.hpp" +#include "squirrel/exposed_object.hpp" + +#include "scripting/sound_object.hpp" + +class ReaderMapping; +class SoundSource; + +/** Plays sound at given interval with specified volume hearable in entire Sector */ +class SoundObject final : public GameObject, + public ExposedObject +{ +public: + SoundObject(const ReaderMapping& mapping); + SoundObject(float vol, const std::string& file); + ~SoundObject() override; + + virtual void draw(DrawingContext& context) override {} + virtual void update(float dt_sec) override {} + + static std::string class_name() { return "sound-object"; } + virtual std::string get_class_name() const override { return class_name(); } + static std::string display_name() { return _("Sound"); } + virtual std::string get_display_name() const override { return display_name(); } + virtual const std::string get_icon_path() const override { return "images/engine/editor/sound.png"; } + + virtual ObjectSettings get_settings() override; + + virtual void stop_looping_sounds() override; + virtual void play_looping_sounds() override; + + /** @name Scriptable methods + @{ */ + void set_volume(float volume); + float get_volume() const { return m_volume; } + /** @} */ + +private: + std::string m_sample; + std::unique_ptr m_sound_source; + float m_volume; + +private: + void prepare_sound_source(); + +private: + SoundObject(const SoundObject&) = delete; + SoundObject& operator=(const SoundObject&) = delete; +}; + +#endif + +/* EOF */ diff --git a/src/scripting/sound_object.cpp b/src/scripting/sound_object.cpp new file mode 100644 index 00000000000..d43442696f8 --- /dev/null +++ b/src/scripting/sound_object.cpp @@ -0,0 +1,53 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "scripting/sound_object.hpp" + +#include "object/sound_object.hpp" + +namespace scripting { + +void +SoundObject::start_playing() +{ + SCRIPT_GUARD_VOID; + object.play_looping_sounds(); +} + +void +SoundObject::stop_playing() +{ + SCRIPT_GUARD_VOID; + object.stop_looping_sounds(); +} + +void +SoundObject::set_volume(float volume) +{ + SCRIPT_GUARD_VOID; + object.set_volume(volume); +} + +float +SoundObject::get_volume() const +{ + SCRIPT_GUARD_DEFAULT; + return object.get_volume(); +} + +} // namespace scripting + +/* EOF */ diff --git a/src/scripting/sound_object.hpp b/src/scripting/sound_object.hpp new file mode 100644 index 00000000000..a6df79b9f5b --- /dev/null +++ b/src/scripting/sound_object.hpp @@ -0,0 +1,74 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HEADER_SUPERTUX_SCRIPTING_SOUND_OBJECT_HPP +#define HEADER_SUPERTUX_SCRIPTING_SOUND_OBJECT_HPP + +#ifndef SCRIPTING_API +#include "scripting/game_object.hpp" + +class SoundObject; +#endif + +namespace scripting { + +/** + * @summary A ""SoundObject"" that was given a name can be controlled by scripts. + * @instances A ""SoundObject"" is instantiated by placing a definition inside a level. + It can then be accessed by its name from a script or via ""sector.name"" from the console. + */ +class SoundObject final +#ifndef SCRIPTING_API + : public GameObject<::SoundObject> +#endif +{ +#ifndef SCRIPTING_API +public: + using GameObject::GameObject; + +private: + SoundObject(const SoundObject&) = delete; + SoundObject& operator=(const SoundObject&) = delete; +#endif + +public: + /** + * Starts playing sound if it was stopped previously. + */ + void start_playing(); + + /** + * Stops playing sound. + */ + void stop_playing(); + + /** + * Sets the volume of sound played by SoundObject. + * @param float $volume + */ + void set_volume(float volume); + + /** + * Returns the volume of sound played by SoundObject. + */ + float get_volume() const; +}; + +} // namespace scripting + +#endif + +/* EOF */ diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index a9f65a75561..7f50cf09b13 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -8571,6 +8571,119 @@ static SQInteger Sector_set_gravity_wrapper(HSQUIRRELVM vm) } +static SQInteger SoundObject_release_hook(SQUserPointer ptr, SQInteger ) +{ + scripting::SoundObject* _this = reinterpret_cast (ptr); + delete _this; + return 0; +} + +static SQInteger SoundObject_start_playing_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'start_playing' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + _this->start_playing(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_playing'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_stop_playing_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'stop_playing' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + _this->stop_playing(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_playing'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_set_volume_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'set_volume' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + SQFloat arg0; + if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) { + sq_throwerror(vm, _SC("Argument 1 not a float")); + return SQ_ERROR; + } + + try { + _this->set_volume(arg0); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_volume'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_get_volume_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'get_volume' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + float return_value = _this->get_volume(); + + sq_pushfloat(vm, return_value); + return 1; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_volume'")); + return SQ_ERROR; + } + +} + static SQInteger Spotlight_release_hook(SQUserPointer ptr, SQInteger ) { scripting::Spotlight* _this = reinterpret_cast (ptr); @@ -13469,6 +13582,32 @@ void create_squirrel_instance(HSQUIRRELVM v, scripting::Sector* object, bool set sq_remove(v, -2); // remove root table } +void create_squirrel_instance(HSQUIRRELVM v, scripting::SoundObject* object, bool setup_releasehook) +{ + using namespace wrapper; + + sq_pushroottable(v); + sq_pushstring(v, "SoundObject", -1); + if(SQ_FAILED(sq_get(v, -2))) { + std::ostringstream msg; + msg << "Couldn't resolved squirrel type 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + + if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) { + std::ostringstream msg; + msg << "Couldn't setup squirrel instance for object of type 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + sq_remove(v, -2); // remove object name + + if(setup_releasehook) { + sq_setreleasehook(v, -1, SoundObject_release_hook); + } + + sq_remove(v, -2); // remove root table +} + void create_squirrel_instance(HSQUIRRELVM v, scripting::Spotlight* object, bool setup_releasehook) { using namespace wrapper; @@ -16417,6 +16556,45 @@ void register_supertux_wrapper(HSQUIRRELVM v) throw SquirrelError(v, "Couldn't register class 'ScriptedObject'"); } + // Register class SoundObject + sq_pushstring(v, "SoundObject", -1); + if(sq_newclass(v, SQFalse) < 0) { + std::ostringstream msg; + msg << "Couldn't create new class 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + sq_pushstring(v, "start_playing", -1); + sq_newclosure(v, &SoundObject_start_playing_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'start_playing'"); + } + + sq_pushstring(v, "stop_playing", -1); + sq_newclosure(v, &SoundObject_stop_playing_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'stop_playing'"); + } + + sq_pushstring(v, "set_volume", -1); + sq_newclosure(v, &SoundObject_set_volume_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, ".b|n"); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'set_volume'"); + } + + sq_pushstring(v, "get_volume", -1); + sq_newclosure(v, &SoundObject_get_volume_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'get_volume'"); + } + + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register class 'SoundObject'"); + } + // Register class Spotlight sq_pushstring(v, "Spotlight", -1); if(sq_newclass(v, SQFalse) < 0) { diff --git a/src/scripting/wrapper.hpp b/src/scripting/wrapper.hpp index d2625106a44..7cfad2044ad 100644 --- a/src/scripting/wrapper.hpp +++ b/src/scripting/wrapper.hpp @@ -58,6 +58,8 @@ class ScriptedObject; void create_squirrel_instance(HSQUIRRELVM v, scripting::ScriptedObject* object, bool setup_releasehook = false); class Sector; void create_squirrel_instance(HSQUIRRELVM v, scripting::Sector* object, bool setup_releasehook = false); +class SoundObject; +void create_squirrel_instance(HSQUIRRELVM v, scripting::SoundObject* object, bool setup_releasehook = false); class Spotlight; void create_squirrel_instance(HSQUIRRELVM v, scripting::Spotlight* object, bool setup_releasehook = false); class Text; diff --git a/src/scripting/wrapper.interface.hpp b/src/scripting/wrapper.interface.hpp index 0827d26202d..7b3ee4deeb3 100644 --- a/src/scripting/wrapper.interface.hpp +++ b/src/scripting/wrapper.interface.hpp @@ -25,6 +25,7 @@ #include "scripting/rock.hpp" #include "scripting/scripted_object.hpp" #include "scripting/sector.hpp" +#include "scripting/sound_object.hpp" #include "scripting/spotlight.hpp" #include "scripting/text.hpp" #include "scripting/text_array.hpp" diff --git a/src/supertux/game_object_factory.cpp b/src/supertux/game_object_factory.cpp index 3e49d90f2fc..cabc900e4ed 100644 --- a/src/supertux/game_object_factory.cpp +++ b/src/supertux/game_object_factory.cpp @@ -117,6 +117,7 @@ #include "object/scripted_object.hpp" #include "object/shard.hpp" #include "object/snow_particle_system.hpp" +#include "object/sound_object.hpp" #include "object/spawnpoint.hpp" #include "object/spotlight.hpp" #include "object/text_array_object.hpp" @@ -277,6 +278,7 @@ GameObjectFactory::init_factories() add_factory("scriptedobject"); add_factory("shard", OBJ_PARAM_DISPENSABLE); add_type_factory("skull_tile", UnstableTile::DELAYED); // Backward compatibility. + add_factory("sound-object"); add_factory("particles-snow"); add_factory("spotlight"); add_factory("textscroller");