audio clips reworked

This commit is contained in:
Mikulas Florek 2020-11-29 17:16:52 +01:00
parent 882ca23d45
commit b5b8657792
7 changed files with 140 additions and 352 deletions

View file

@ -22,13 +22,6 @@ static const ComponentType AMBIENT_SOUND_TYPE = Reflection::getComponentType("am
static const ComponentType ECHO_ZONE_TYPE = Reflection::getComponentType("echo_zone"); static const ComponentType ECHO_ZONE_TYPE = Reflection::getComponentType("echo_zone");
static const ComponentType CHORUS_ZONE_TYPE = Reflection::getComponentType("chorus_zone"); static const ComponentType CHORUS_ZONE_TYPE = Reflection::getComponentType("chorus_zone");
enum class AudioSceneVersion : int
{
LAST
};
struct Listener struct Listener
{ {
EntityPtr entity; EntityPtr entity;
@ -38,7 +31,7 @@ struct Listener
struct AmbientSound struct AmbientSound
{ {
EntityRef entity; EntityRef entity;
AudioScene::ClipInfo* clip; Clip* clip = nullptr;
bool is_3d; bool is_3d;
int playing_sound; int playing_sound;
}; };
@ -48,17 +41,22 @@ struct PlayingSound
{ {
AudioDevice::BufferHandle buffer_id; AudioDevice::BufferHandle buffer_id;
EntityPtr entity; EntityPtr entity;
AudioScene::ClipInfo* clip; Clip* clip = nullptr;
bool is_3d; bool is_3d;
}; };
struct AudioSceneImpl final : AudioScene struct AudioSceneImpl final : AudioScene
{ {
enum class Version : i32 {
INIT,
CLIPS_REWORKED,
LATEST
};
AudioSceneImpl(AudioSystem& system, Universe& context, IAllocator& allocator) AudioSceneImpl(AudioSystem& system, Universe& context, IAllocator& allocator)
: m_allocator(allocator) : m_allocator(allocator)
, m_universe(context) , m_universe(context)
, m_clips(allocator)
, m_system(system) , m_system(system)
, m_device(system.getDevice()) , m_device(system.getDevice())
, m_ambient_sounds(allocator) , m_ambient_sounds(allocator)
@ -90,34 +88,20 @@ struct AudioSceneImpl final : AudioScene
} }
int getVersion() const override { return (int)AudioSceneVersion::LAST; } i32 getVersion() const override { return (i32)Version::LATEST; }
void clear() override void clear() override
{ {
for (auto* clip : m_clips)
{
clip->clip->decRefCount();
LUMIX_DELETE(m_allocator, clip);
}
m_clips.clear();
m_ambient_sounds.clear(); m_ambient_sounds.clear();
m_echo_zones.clear(); m_echo_zones.clear();
m_chorus_zones.clear(); m_chorus_zones.clear();
} }
int playSound(EntityRef entity, const char* clip_name, bool is_3d) override
{
auto* clip = getClipInfo(clip_name);
if (clip) return play(entity, clip, is_3d);
return -1;
}
void updateAnimationEvents() void updateAnimationEvents()
{ {
if (!m_animation_scene) return; /*if (!m_animation_scene) return;
InputMemoryStream blob(m_animation_scene->getEventStream()); InputMemoryStream blob(m_animation_scene->getEventStream());
u32 sound_type = crc32("sound"); u32 sound_type = crc32("sound");
@ -143,7 +127,7 @@ struct AudioSceneImpl final : AudioScene
{ {
blob.skip(size); blob.skip(size);
} }
} }*/
} }
void update(float time_delta, bool paused) override void update(float time_delta, bool paused) override
@ -170,8 +154,8 @@ struct AudioSceneImpl final : AudioScene
m_device.setSourcePosition(sound.buffer_id, pos); m_device.setSourcePosition(sound.buffer_id, pos);
} }
auto* clip_info = sound.clip; Clip* clip_info = sound.clip;
if (!clip_info->looped && m_device.isEnd(sound.buffer_id)) if (!clip_info->m_looped && m_device.isEnd(sound.buffer_id))
{ {
m_device.stop(sound.buffer_id); m_device.stop(sound.buffer_id);
sound.buffer_id = AudioDevice::INVALID_BUFFER_HANDLE; sound.buffer_id = AudioDevice::INVALID_BUFFER_HANDLE;
@ -231,37 +215,20 @@ struct AudioSceneImpl final : AudioScene
} }
ClipInfo* getAmbientSoundClip(EntityRef entity) override Path getAmbientSoundClip(EntityRef entity) override
{ {
return m_ambient_sounds[entity].clip; AmbientSound& snd = m_ambient_sounds[entity];
return snd.clip ? snd.clip->getPath() : Path();
} }
int getClipInfoIndex(ClipInfo* info) override void setAmbientSoundClip(EntityRef entity, const Path& clip) override
{ {
for (int i = 0; i < m_clips.size(); ++i) Clip* res = m_system.getEngine().getResourceManager().load<Clip>(clip);
{ if (m_ambient_sounds[entity].clip) {
if (m_clips[i] == info) return i; m_ambient_sounds[entity].clip->decRefCount();
} }
return -1; m_ambient_sounds[entity].clip = res;
}
int getAmbientSoundClipIndex(EntityRef entity) override
{
return m_clips.indexOf(m_ambient_sounds[entity].clip);
}
void setAmbientSoundClipIndex(EntityRef entity, int index) override
{
m_ambient_sounds[entity].clip = m_clips[index];
}
void setAmbientSoundClip(EntityRef entity, ClipInfo* clip) override
{
m_ambient_sounds[entity].clip = clip;
} }
@ -345,22 +312,11 @@ struct AudioSceneImpl final : AudioScene
void serialize(OutputMemoryStream& serializer) override void serialize(OutputMemoryStream& serializer) override
{ {
serializer.write(m_listener.entity); serializer.write(m_listener.entity);
serializer.write(m_clips.size());
for (auto* clip : m_clips)
{
serializer.write(clip != nullptr);
if (!clip) continue;
serializer.write(clip->volume);
serializer.write(clip->looped);
serializer.writeString(clip->name);
serializer.writeString(clip->clip->getPath().c_str());
}
serializer.write(m_ambient_sounds.size()); serializer.write(m_ambient_sounds.size());
for (const AmbientSound& sound : m_ambient_sounds) for (const AmbientSound& sound : m_ambient_sounds)
{ {
serializer.write(m_clips.indexOf(sound.clip)); serializer.writeString(sound.clip ? sound.clip->getPath().c_str() : "");
serializer.write(sound.entity); serializer.write(sound.entity);
serializer.write(sound.is_3d); serializer.write(sound.is_3d);
} }
@ -387,36 +343,20 @@ struct AudioSceneImpl final : AudioScene
m_universe.onComponentCreated((EntityRef)m_listener.entity, LISTENER_TYPE, this); m_universe.onComponentCreated((EntityRef)m_listener.entity, LISTENER_TYPE, this);
} }
int count = 0; if (version < (i32)Version::CLIPS_REWORKED) {
serializer.read(count); int dummy;
const u32 clip_offset = m_clips.size(); serializer.read(dummy);
m_clips.reserve(m_clips.size() + count); ASSERT(dummy == 0);
for (int i = 0; i < count; ++i) {
bool is_valid;
serializer.read(is_valid);
if (!is_valid) {
m_clips.emplace() = nullptr;
continue;
}
auto* clip = LUMIX_NEW(m_allocator, ClipInfo);
m_clips.emplace() = clip;
clip->volume = 1;
serializer.read(clip->volume);
serializer.read(clip->looped);
copyString(clip->name, serializer.readString());
clip->name_hash = crc32(clip->name);
const char* path = serializer.readString();
clip->clip = m_system.getEngine().getResourceManager().load<Clip>(Path(path));
} }
i32 count;
serializer.read(count); serializer.read(count);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
AmbientSound sound; AmbientSound sound;
int clip_idx; ASSERT(version >= (i32)Version::CLIPS_REWORKED);
serializer.read(clip_idx); const char* path = serializer.readString();
if (clip_idx >= 0) sound.clip = m_clips[clip_idx + clip_offset]; Clip* res = path[0] ? m_system.getEngine().getResourceManager().load<Clip>(Path(path)) : nullptr;
sound.clip = res;
serializer.read(sound.entity); serializer.read(sound.entity);
sound.entity = entity_map.get(sound.entity); sound.entity = entity_map.get(sound.entity);
serializer.read(sound.is_3d); serializer.read(sound.is_3d);
@ -447,112 +387,22 @@ struct AudioSceneImpl final : AudioScene
} }
} }
SoundHandle play(EntityRef entity, const Path& clip, bool is_3d) override {
u32 getClipCount() const override { return m_clips.size(); } Clip* res = m_system.getEngine().getResourceManager().load<Clip>(clip);
return play(entity, res, is_3d);
const char* getClipName(u32 index) override { return m_clips[index]->name; }
void addClip(const char* name, const Path& path) override
{
auto* clip = LUMIX_NEW(m_allocator, ClipInfo);
copyString(clip->name, name);
clip->name_hash = crc32(name);
clip->clip = m_system.getEngine().getResourceManager().load<Clip>(path);
clip->looped = false;
clip->volume = 1;
m_clips.push(clip);
} }
SoundHandle play(EntityRef entity, Clip* clip, bool is_3d) {
void removeClip(ClipInfo* info) override for (PlayingSound& sound : m_playing_sounds) {
{ if (sound.buffer_id == AudioDevice::INVALID_BUFFER_HANDLE) {
for (PlayingSound& i : m_playing_sounds)
{
if (i.clip == info && i.buffer_id != AudioDevice::INVALID_BUFFER_HANDLE)
{
m_device.stop(i.buffer_id);
i.buffer_id = AudioDevice::INVALID_BUFFER_HANDLE;
}
}
for (AmbientSound& sound : m_ambient_sounds)
{
if (sound.clip == info)
{
sound.clip = nullptr;
sound.playing_sound = -1;
}
}
Clip* clip = info->clip;
if (clip)
{
clip->decRefCount();
}
LUMIX_DELETE(m_allocator, info);
m_clips.eraseItem(info);
}
ClipInfo* getClipInfo(const char* name) override
{
auto hash = crc32(name);
for (auto* i : m_clips)
{
if (i->name_hash == hash) return i;
}
return nullptr;
}
ClipInfo* getClipInfo(u32 hash) override
{
for (auto* i : m_clips)
{
if (i->name_hash == hash) return i;
}
return nullptr;
}
ClipInfo* getClipInfoByIndex(u32 index) override
{
if (index >= (u32)m_clips.size()) return nullptr;
return m_clips[index];
}
void setClip(u32 clip_id, const Path& path) override
{
auto* clip = m_clips[clip_id]->clip;
if (clip)
{
clip->decRefCount();
}
auto* new_res = m_system.getEngine().getResourceManager().load<Clip>(path);
m_clips[clip_id]->clip = static_cast<Clip*>(new_res);
}
SoundHandle play(EntityRef entity, ClipInfo* clip_info, bool is_3d) override
{
for (PlayingSound& sound : m_playing_sounds)
{
if (sound.buffer_id == AudioDevice::INVALID_BUFFER_HANDLE)
{
auto* clip = clip_info->clip;
if (!clip->isReady()) return INVALID_SOUND_HANDLE; if (!clip->isReady()) return INVALID_SOUND_HANDLE;
int flags = is_3d ? (int)AudioDevice::BufferFlags::IS3D : 0; int flags = is_3d ? (int)AudioDevice::BufferFlags::IS3D : 0;
auto buffer = m_device.createBuffer( auto buffer = m_device.createBuffer(clip->getData(), clip->getSize(), clip->getChannels(), clip->getSampleRate(), flags);
clip->getData(), clip->getSize(), clip->getChannels(), clip->getSampleRate(), flags);
if (buffer == AudioDevice::INVALID_BUFFER_HANDLE) return INVALID_SOUND_HANDLE; if (buffer == AudioDevice::INVALID_BUFFER_HANDLE) return INVALID_SOUND_HANDLE;
m_device.play(buffer, clip_info->looped);
m_device.setVolume(buffer, clip_info->volume); m_device.play(buffer, clip->m_looped);
m_device.setVolume(buffer, clip->m_volume);
const DVec3 pos = m_universe.getPosition(entity); const DVec3 pos = m_universe.getPosition(entity);
m_device.setSourcePosition(buffer, pos); m_device.setSourcePosition(buffer, pos);
@ -560,10 +410,10 @@ struct AudioSceneImpl final : AudioScene
sound.is_3d = is_3d; sound.is_3d = is_3d;
sound.buffer_id = buffer; sound.buffer_id = buffer;
sound.entity = entity; sound.entity = entity;
sound.clip = clip_info; clip->incRefCount();
sound.clip = clip;
for (const EchoZone& zone : m_echo_zones) for (const EchoZone& zone : m_echo_zones) {
{
const double dist2 = (pos - m_universe.getPosition(zone.entity)).squaredLength(); const double dist2 = (pos - m_universe.getPosition(zone.entity)).squaredLength();
const double r2 = zone.radius * zone.radius; const double r2 = zone.radius * zone.radius;
if (dist2 > r2) continue; if (dist2 > r2) continue;
@ -573,8 +423,7 @@ struct AudioSceneImpl final : AudioScene
break; break;
} }
for (const ChorusZone& zone : m_chorus_zones) for (const ChorusZone& zone : m_chorus_zones) {
{
const double dist2 = (pos - m_universe.getPosition(zone.entity)).squaredLength(); const double dist2 = (pos - m_universe.getPosition(zone.entity)).squaredLength();
double r2 = zone.radius * zone.radius; double r2 = zone.radius * zone.radius;
if (dist2 > r2) continue; if (dist2 > r2) continue;
@ -596,6 +445,10 @@ struct AudioSceneImpl final : AudioScene
ASSERT(sound_id >= 0 && sound_id < (int)lengthOf(m_playing_sounds)); ASSERT(sound_id >= 0 && sound_id < (int)lengthOf(m_playing_sounds));
m_device.stop(m_playing_sounds[sound_id].buffer_id); m_device.stop(m_playing_sounds[sound_id].buffer_id);
m_playing_sounds[sound_id].buffer_id = AudioDevice::INVALID_BUFFER_HANDLE; m_playing_sounds[sound_id].buffer_id = AudioDevice::INVALID_BUFFER_HANDLE;
if (m_playing_sounds[sound_id].clip) {
m_playing_sounds[sound_id].clip->decRefCount();
m_playing_sounds[sound_id].clip = nullptr;
}
} }
@ -626,7 +479,6 @@ struct AudioSceneImpl final : AudioScene
Listener m_listener; Listener m_listener;
IAllocator& m_allocator; IAllocator& m_allocator;
Universe& m_universe; Universe& m_universe;
Array<ClipInfo*> m_clips;
AudioSystem& m_system; AudioSystem& m_system;
PlayingSound m_playing_sounds[AudioDevice::MAX_PLAYING_SOUNDS]; PlayingSound m_playing_sounds[AudioDevice::MAX_PLAYING_SOUNDS];
AnimationScene* m_animation_scene = nullptr; AnimationScene* m_animation_scene = nullptr;

View file

@ -50,43 +50,21 @@ struct AudioScene : IScene
using SoundHandle = i32; using SoundHandle = i32;
static constexpr SoundHandle INVALID_SOUND_HANDLE = -1; static constexpr SoundHandle INVALID_SOUND_HANDLE = -1;
struct ClipInfo
{
Clip* clip;
char name[30];
u32 name_hash;
bool looped;
float volume = 1;
};
static UniquePtr<AudioScene> createInstance(AudioSystem& system, static UniquePtr<AudioScene> createInstance(AudioSystem& system,
Universe& universe, Universe& universe,
struct IAllocator& allocator); struct IAllocator& allocator);
virtual void setMasterVolume(float volume) = 0; virtual void setMasterVolume(float volume) = 0;
virtual u32 getClipCount() const = 0;
virtual const char* getClipName(u32 index) = 0;
virtual ClipInfo* getClipInfoByIndex(u32 index) = 0;
virtual ClipInfo* getClipInfo(u32 hash) = 0;
virtual ClipInfo* getClipInfo(const char* name) = 0;
virtual int getClipInfoIndex(ClipInfo* info) = 0;
virtual void addClip(const char* name, const Path& path) = 0;
virtual void removeClip(ClipInfo* clip) = 0;
virtual void setClip(u32 clip_id, const Path& path) = 0;
virtual EchoZone& getEchoZone(EntityRef entity) = 0; virtual EchoZone& getEchoZone(EntityRef entity) = 0;
virtual ChorusZone& getChorusZone(EntityRef entity) = 0; virtual ChorusZone& getChorusZone(EntityRef entity) = 0;
virtual ClipInfo* getAmbientSoundClip(EntityRef entity) = 0; virtual Path getAmbientSoundClip(EntityRef entity) = 0;
virtual int getAmbientSoundClipIndex(EntityRef entity) = 0; virtual void setAmbientSoundClip(EntityRef entity, const Path& clip) = 0;
virtual void setAmbientSoundClipIndex(EntityRef entity, int index) = 0;
virtual void setAmbientSoundClip(EntityRef entity, ClipInfo* clip) = 0;
virtual bool isAmbientSound3D(EntityRef entity) = 0; virtual bool isAmbientSound3D(EntityRef entity) = 0;
virtual void setAmbientSound3D(EntityRef entity, bool is_3d) = 0; virtual void setAmbientSound3D(EntityRef entity, bool is_3d) = 0;
virtual SoundHandle playSound(EntityRef entity, const char* clip_name, bool is_3d) = 0; virtual SoundHandle play(EntityRef entity, const Path& clip, bool is_3d) = 0;
virtual SoundHandle play(EntityRef entity, ClipInfo* clip, bool is_3d) = 0;
virtual void stop(SoundHandle sound_id) = 0; virtual void stop(SoundHandle sound_id) = 0;
virtual void setVolume(SoundHandle sound_id, float volume) = 0; virtual void setVolume(SoundHandle sound_id, float volume) = 0;

View file

@ -12,25 +12,23 @@
namespace Lumix namespace Lumix
{ {
namespace Reflection {
//inline AudioScene::SoundHandle fromVariant(int i, Span<Variant> args, VariantTag<AudioScene::SoundHandle>) { return args[i].i; }
}
static void registerProperties(IAllocator& allocator) static void registerProperties(IAllocator& allocator)
{ {
struct ClipIndexEnum : Reflection::EnumAttribute {
u32 count(ComponentUID cmp) const override { return ((AudioScene*)cmp.scene)->getClipCount(); }
const char* name(ComponentUID cmp, u32 idx) const override { return ((AudioScene*)cmp.scene)->getClipName(idx); }
};
using namespace Reflection; using namespace Reflection;
static auto audio_scene = scene("audio", static auto audio_scene = scene("audio",
functions( functions(
LUMIX_FUNC(AudioScene::setMasterVolume), LUMIX_FUNC(AudioScene::setMasterVolume),
LUMIX_FUNC(AudioScene::playSound), LUMIX_FUNC(AudioScene::play),
LUMIX_FUNC(AudioScene::setVolume), LUMIX_FUNC(AudioScene::setVolume),
LUMIX_FUNC(AudioScene::setEcho) LUMIX_FUNC(AudioScene::setEcho)
), ),
component("ambient_sound", component("ambient_sound",
property("3D", &AudioScene::isAmbientSound3D, &AudioScene::setAmbientSound3D), property("3D", &AudioScene::isAmbientSound3D, &AudioScene::setAmbientSound3D),
property("Sound", LUMIX_PROP(AudioScene, AmbientSoundClipIndex), ClipIndexEnum()) property("Sound", LUMIX_PROP(AudioScene, AmbientSoundClip), ResourceAttribute("OGG (*.ogg)", Clip::TYPE))
), ),
component("audio_listener"), component("audio_listener"),
component("echo_zone", component("echo_zone",

View file

@ -5,6 +5,7 @@
#include "engine/profiler.h" #include "engine/profiler.h"
#include "engine/resource.h" #include "engine/resource.h"
#include "engine/string.h" #include "engine/string.h"
#include "engine/stream.h"
#define STB_VORBIS_HEADER_ONLY #define STB_VORBIS_HEADER_ONLY
#include "stb/stb_vorbis.cpp" #include "stb/stb_vorbis.cpp"
@ -25,8 +26,18 @@ void Clip::unload()
bool Clip::load(u64 size, const u8* mem) bool Clip::load(u64 size, const u8* mem)
{ {
PROFILE_FUNCTION(); PROFILE_FUNCTION();
InputMemoryStream blob(mem, size);
const u32 version = blob.read<u32>();
if (version != 0) return false;
const Format format = blob.read<Format>();
if (format != Format::OGG) return false;
m_looped = blob.read<bool>();
m_volume = blob.read<float>();
short* output = nullptr; short* output = nullptr;
auto res = stb_vorbis_decode_memory((unsigned char*)mem, (int)size, &m_channels, &m_sample_rate, &output); auto res = stb_vorbis_decode_memory((unsigned char*)blob.skip(0), (int)(size - blob.getPosition()), &m_channels, &m_sample_rate, &output);
if (res <= 0) return false; if (res <= 0) return false;
m_data.resize(res * m_channels); m_data.resize(res * m_channels);

View file

@ -13,6 +13,10 @@ namespace Lumix
struct Clip final : Resource struct Clip final : Resource
{ {
public: public:
enum class Format : u8 {
OGG
};
Clip(const Path& path, ResourceManager& manager, IAllocator& allocator) Clip(const Path& path, ResourceManager& manager, IAllocator& allocator)
: Resource(path, manager, allocator) : Resource(path, manager, allocator)
, m_data(allocator) , m_data(allocator)
@ -30,6 +34,8 @@ public:
float getLengthSeconds() const { return m_data.size() / float(m_channels * m_sample_rate); } float getLengthSeconds() const { return m_data.size() / float(m_channels * m_sample_rate); }
static const ResourceType TYPE; static const ResourceType TYPE;
bool m_looped = false;
float m_volume = 1;
private: private:
int m_channels; int m_channels;

View file

@ -11,6 +11,7 @@
#include "editor/world_editor.h" #include "editor/world_editor.h"
#include "engine/crc32.h" #include "engine/crc32.h"
#include "engine/engine.h" #include "engine/engine.h"
#include "engine/lua_wrapper.h"
#include "engine/reflection.h" #include "engine/reflection.h"
#include "engine/universe.h" #include "engine/universe.h"
@ -22,7 +23,7 @@ namespace
{ {
struct AssetBrowserPlugin final : AssetBrowser::IPlugin struct AssetBrowserPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
{ {
explicit AssetBrowserPlugin(StudioApp& app) explicit AssetBrowserPlugin(StudioApp& app)
: m_app(app) : m_app(app)
@ -32,6 +33,36 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
app.getAssetCompiler().registerExtension("ogg", Clip::TYPE); app.getAssetCompiler().registerExtension("ogg", Clip::TYPE);
} }
struct Meta {
bool looped = true;
float volume = 1.f;
};
Meta getMeta(const Path& path) const {
Meta meta;
m_app.getAssetCompiler().getMeta(path, [&path, &meta](lua_State* L){
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "looped", &meta.looped);
LuaWrapper::getOptionalField(L, LUA_GLOBALSINDEX, "volume", &meta.volume);
});
return meta;
}
bool compile(const Path& src) override {
FileSystem& fs = m_app.getEngine().getFileSystem();
OutputMemoryStream src_data(m_app.getAllocator());
if (!fs.getContentSync(src, Ref(src_data))) return false;
Meta meta = getMeta(src);
OutputMemoryStream compiled(m_app.getAllocator());
compiled.reserve(64 + src_data.size());
compiled.write((u32)0);
compiled.write(Clip::Format::OGG);
compiled.write(meta.looped);
compiled.write(meta.volume);
compiled.write(src_data.data(), src_data.size());
return m_app.getAssetCompiler().writeCompiledResource(src.c_str(), Span(compiled.data(), (i32)compiled.size()));
}
static AudioDevice& getAudioDevice(Engine& engine) static AudioDevice& getAudioDevice(Engine& engine)
{ {
@ -56,6 +87,16 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
{ {
if(resources.length() > 1) return; if(resources.length() > 1) return;
if(resources[0]->getPath().getHash() != m_meta_res) {
m_meta = getMeta(resources[0]->getPath());
m_meta_res = resources[0]->getPath().getHash();
}
ImGuiEx::Label("Looped");
ImGui::Checkbox("##loop", &m_meta.looped);
ImGuiEx::Label("Volume");
ImGui::DragFloat("##vol", &m_meta.volume, 0.01f, 0, FLT_MAX);
auto* clip = static_cast<Clip*>(resources[0]); auto* clip = static_cast<Clip*>(resources[0]);
ImGuiEx::Label("Length"); ImGuiEx::Label("Length");
ImGui::Text("%f", clip->getLengthSeconds()); ImGui::Text("%f", clip->getLengthSeconds());
@ -63,7 +104,7 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
if (m_playing_clip >= 0) if (m_playing_clip >= 0)
{ {
if (ImGui::Button("Stop")) if (ImGui::Button(ICON_FA_STOP "Stop"))
{ {
stopAudio(); stopAudio();
return; return;
@ -76,7 +117,7 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
} }
} }
if (m_playing_clip < 0 && ImGui::Button("Play")) if (m_playing_clip < 0 && ImGui::Button(ICON_FA_PLAY "Play"))
{ {
stopAudio(); stopAudio();
@ -85,6 +126,19 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
device.play(handle, true); device.play(handle, true);
m_playing_clip = handle; m_playing_clip = handle;
} }
ImGui::SameLine();
if (ImGui::Button(ICON_FA_CHECK "Apply")) {
const StaticString<512> src("volume = ", m_meta.volume
, "\nlooped = ", m_meta.looped ? "true" : "false"
);
AssetCompiler& compiler = m_app.getAssetCompiler();
compiler.updateMeta(resources[0]->getPath(), src);
if (compiler.compile(resources[0]->getPath())) {
resources[0]->getResourceManager().reload(*resources[0]);
}
}
} }
@ -105,119 +159,8 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
int m_playing_clip; int m_playing_clip;
StudioApp& m_app; StudioApp& m_app;
AssetBrowser& m_browser; AssetBrowser& m_browser;
}; Meta m_meta;
u32 m_meta_res = 0;
struct ClipManagerUI final : StudioApp::GUIPlugin
{
explicit ClipManagerUI(StudioApp& app)
: m_app(app)
{
m_filter[0] = 0;
m_is_open = false;
m_toggle_ui.init("Clip manager", "Toggle clip manager", "clip_manager", "", true);
m_toggle_ui.func.bind<&ClipManagerUI::onAction>(this);
m_toggle_ui.is_selected.bind<&ClipManagerUI::isOpen>(this);
app.addWindowAction(&m_toggle_ui);
}
~ClipManagerUI() {
m_app.removeAction(&m_toggle_ui);
}
// TODO
/*
void pluginAdded(GUIPlugin& plugin) override
{
if (!equalStrings(plugin.getName(), "animation_editor")) return;
auto& anim_editor = (AnimEditor::IAnimationEditor&)plugin;
auto& event_type = anim_editor.createEventType("sound");
event_type.size = sizeof(SoundAnimationEvent);
event_type.label = "Sound";
event_type.editor.bind<ClipManagerUI, &ClipManagerUI::onSoundEventGUI>(this);
}
void onSoundEventGUI(u8* data, AnimEditor::Component& component) const
{
auto* ev = (SoundAnimationEvent*)data;
AudioScene* scene = (AudioScene*)m_app.getWorldEditor().getUniverse()->getScene(crc32("audio"));
auto getter = [](void* data, int idx, const char** out) -> bool {
auto* scene = (AudioScene*)data;
*out = scene->getClipName(idx);
return true;
};
AudioScene::ClipInfo* clip = scene->getClipInfo(ev->clip);
int current = clip ? scene->getClipInfoIndex(clip) : -1;
if (ImGui::Combo("Clip", &current, getter, scene, scene->getClipCount()))
{
ev->clip = scene->getClipInfo(current)->name_hash;
}
}
*/
const char* getName() const override { return "audio"; }
bool isOpen() const { return m_is_open; }
void onAction() { m_is_open = !m_is_open; }
void onWindowGUI() override
{
if (!m_is_open) return;
if (ImGui::Begin("Clip Manager", &m_is_open)) {
ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##filter", "Filter", m_filter, sizeof(m_filter));
Universe* universe = m_app.getWorldEditor().getUniverse();
auto* audio_scene = static_cast<AudioScene*>(universe->getScene(crc32("audio")));
u32 clip_count = audio_scene->getClipCount();
for (u32 clip_id = 0; clip_id < clip_count; ++clip_id) {
AudioScene::ClipInfo* clip_info = audio_scene->getClipInfoByIndex(clip_id);
if (!clip_info) continue;
if (m_filter[0] != 0 && stristr(clip_info->name, m_filter) == nullptr) {
continue;
}
if (ImGui::TreeNode((const void*)(uintptr)clip_id, "%s", clip_info->name)) {
if (ImGui::InputText("Name", clip_info->name, sizeof(clip_info->name))) {
clip_info->name_hash = crc32(clip_info->name);
}
char path[MAX_PATH_LENGTH];
copyString(path, clip_info->clip ? clip_info->clip->getPath().c_str() : "");
ImGuiEx::Label("Clip");
if (m_app.getAssetBrowser().resourceInput("clip", Span(path), Clip::TYPE)) {
audio_scene->setClip(clip_id, Path(path));
}
ImGuiEx::Label("Volume");
ImGui::InputFloat("##volume", &clip_info->volume);
ImGuiEx::Label("Looped");
ImGui::Checkbox("##looped", &clip_info->looped);
if (ImGui::Button("Remove")) {
audio_scene->removeClip(clip_info);
--clip_count;
}
ImGui::TreePop();
}
}
if (ImGui::Button("Add")) {
audio_scene->addClip("test", Path("test.ogg"));
}
}
ImGui::End();
}
StudioApp& m_app;
char m_filter[256];
bool m_is_open;
Action m_toggle_ui;
}; };
@ -226,7 +169,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
explicit StudioAppPlugin(StudioApp& app) explicit StudioAppPlugin(StudioApp& app)
: m_app(app) : m_app(app)
, m_asset_browser_plugin(app) , m_asset_browser_plugin(app)
, m_clip_manager_ui(app)
{} {}
const char* getName() const override { return "audio"; } const char* getName() const override { return "audio"; }
@ -241,8 +183,8 @@ struct StudioAppPlugin : StudioApp::IPlugin
IAllocator& allocator = m_app.getAllocator(); IAllocator& allocator = m_app.getAllocator();
m_app.getAssetBrowser().addPlugin(m_asset_browser_plugin); m_app.getAssetBrowser().addPlugin(m_asset_browser_plugin);
const char* extensions[] = { "ogg", nullptr };
m_app.addPlugin(m_clip_manager_ui); m_app.getAssetCompiler().addPlugin(m_asset_browser_plugin, extensions);
} }
@ -280,13 +222,12 @@ struct StudioAppPlugin : StudioApp::IPlugin
~StudioAppPlugin() ~StudioAppPlugin()
{ {
m_app.getAssetBrowser().removePlugin(m_asset_browser_plugin); m_app.getAssetBrowser().removePlugin(m_asset_browser_plugin);
m_app.removePlugin(m_clip_manager_ui); m_app.getAssetCompiler().removePlugin(m_asset_browser_plugin);
} }
StudioApp& m_app; StudioApp& m_app;
AssetBrowserPlugin m_asset_browser_plugin; AssetBrowserPlugin m_asset_browser_plugin;
ClipManagerUI m_clip_manager_ui;
}; };

View file

@ -660,12 +660,14 @@ inline Variant::Type _getVariantType(VariantTag<EntityPtr>) { return Variant::EN
inline Variant::Type _getVariantType(VariantTag<EntityRef>) { return Variant::ENTITY; } inline Variant::Type _getVariantType(VariantTag<EntityRef>) { return Variant::ENTITY; }
inline Variant::Type _getVariantType(VariantTag<Vec2>) { return Variant::VEC2; } inline Variant::Type _getVariantType(VariantTag<Vec2>) { return Variant::VEC2; }
inline Variant::Type _getVariantType(VariantTag<Vec3>) { return Variant::VEC3; } inline Variant::Type _getVariantType(VariantTag<Vec3>) { return Variant::VEC3; }
inline Variant::Type _getVariantType(VariantTag<Path>) { return Variant::CSTR; }
inline Variant::Type _getVariantType(VariantTag<DVec3>) { return Variant::DVEC3; } inline Variant::Type _getVariantType(VariantTag<DVec3>) { return Variant::DVEC3; }
template <typename T> inline Variant::Type getVariantType() { return _getVariantType(VariantTag<RemoveCVR<T>>{}); } template <typename T> inline Variant::Type getVariantType() { return _getVariantType(VariantTag<RemoveCVR<T>>{}); }
inline bool fromVariant(int i, Span<Variant> args, VariantTag<bool>) { return args[i].b; } inline bool fromVariant(int i, Span<Variant> args, VariantTag<bool>) { return args[i].b; }
inline float fromVariant(int i, Span<Variant> args, VariantTag<float>) { return args[i].f; } inline float fromVariant(int i, Span<Variant> args, VariantTag<float>) { return args[i].f; }
inline const char* fromVariant(int i, Span<Variant> args, VariantTag<const char*>) { return args[i].s; } inline const char* fromVariant(int i, Span<Variant> args, VariantTag<const char*>) { return args[i].s; }
inline Path fromVariant(int i, Span<Variant> args, VariantTag<Path>) { return Path(args[i].s); }
inline i32 fromVariant(int i, Span<Variant> args, VariantTag<i32>) { return args[i].i; } inline i32 fromVariant(int i, Span<Variant> args, VariantTag<i32>) { return args[i].i; }
inline u32 fromVariant(int i, Span<Variant> args, VariantTag<u32>) { return args[i].u; } inline u32 fromVariant(int i, Span<Variant> args, VariantTag<u32>) { return args[i].u; }
inline Vec2 fromVariant(int i, Span<Variant> args, VariantTag<Vec2>) { return args[i].v2; } inline Vec2 fromVariant(int i, Span<Variant> args, VariantTag<Vec2>) { return args[i].v2; }