audio clips reworked
This commit is contained in:
parent
882ca23d45
commit
b5b8657792
7 changed files with 140 additions and 352 deletions
|
@ -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 CHORUS_ZONE_TYPE = Reflection::getComponentType("chorus_zone");
|
||||
|
||||
|
||||
enum class AudioSceneVersion : int
|
||||
{
|
||||
LAST
|
||||
};
|
||||
|
||||
|
||||
struct Listener
|
||||
{
|
||||
EntityPtr entity;
|
||||
|
@ -38,7 +31,7 @@ struct Listener
|
|||
struct AmbientSound
|
||||
{
|
||||
EntityRef entity;
|
||||
AudioScene::ClipInfo* clip;
|
||||
Clip* clip = nullptr;
|
||||
bool is_3d;
|
||||
int playing_sound;
|
||||
};
|
||||
|
@ -48,17 +41,22 @@ struct PlayingSound
|
|||
{
|
||||
AudioDevice::BufferHandle buffer_id;
|
||||
EntityPtr entity;
|
||||
AudioScene::ClipInfo* clip;
|
||||
Clip* clip = nullptr;
|
||||
bool is_3d;
|
||||
};
|
||||
|
||||
|
||||
struct AudioSceneImpl final : AudioScene
|
||||
{
|
||||
enum class Version : i32 {
|
||||
INIT,
|
||||
CLIPS_REWORKED,
|
||||
LATEST
|
||||
};
|
||||
|
||||
AudioSceneImpl(AudioSystem& system, Universe& context, IAllocator& allocator)
|
||||
: m_allocator(allocator)
|
||||
, m_universe(context)
|
||||
, m_clips(allocator)
|
||||
, m_system(system)
|
||||
, m_device(system.getDevice())
|
||||
, 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
|
||||
{
|
||||
for (auto* clip : m_clips)
|
||||
{
|
||||
clip->clip->decRefCount();
|
||||
LUMIX_DELETE(m_allocator, clip);
|
||||
}
|
||||
m_clips.clear();
|
||||
m_ambient_sounds.clear();
|
||||
m_echo_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()
|
||||
{
|
||||
if (!m_animation_scene) return;
|
||||
/*if (!m_animation_scene) return;
|
||||
|
||||
InputMemoryStream blob(m_animation_scene->getEventStream());
|
||||
u32 sound_type = crc32("sound");
|
||||
|
@ -143,7 +127,7 @@ struct AudioSceneImpl final : AudioScene
|
|||
{
|
||||
blob.skip(size);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void update(float time_delta, bool paused) override
|
||||
|
@ -170,8 +154,8 @@ struct AudioSceneImpl final : AudioScene
|
|||
m_device.setSourcePosition(sound.buffer_id, pos);
|
||||
}
|
||||
|
||||
auto* clip_info = sound.clip;
|
||||
if (!clip_info->looped && m_device.isEnd(sound.buffer_id))
|
||||
Clip* clip_info = sound.clip;
|
||||
if (!clip_info->m_looped && m_device.isEnd(sound.buffer_id))
|
||||
{
|
||||
m_device.stop(sound.buffer_id);
|
||||
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)
|
||||
{
|
||||
if (m_clips[i] == info) return i;
|
||||
Clip* res = m_system.getEngine().getResourceManager().load<Clip>(clip);
|
||||
if (m_ambient_sounds[entity].clip) {
|
||||
m_ambient_sounds[entity].clip->decRefCount();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
m_ambient_sounds[entity].clip = res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -345,22 +312,11 @@ struct AudioSceneImpl final : AudioScene
|
|||
void serialize(OutputMemoryStream& serializer) override
|
||||
{
|
||||
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());
|
||||
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.is_3d);
|
||||
}
|
||||
|
@ -387,36 +343,20 @@ struct AudioSceneImpl final : AudioScene
|
|||
m_universe.onComponentCreated((EntityRef)m_listener.entity, LISTENER_TYPE, this);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
serializer.read(count);
|
||||
const u32 clip_offset = m_clips.size();
|
||||
m_clips.reserve(m_clips.size() + count);
|
||||
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));
|
||||
if (version < (i32)Version::CLIPS_REWORKED) {
|
||||
int dummy;
|
||||
serializer.read(dummy);
|
||||
ASSERT(dummy == 0);
|
||||
}
|
||||
|
||||
i32 count;
|
||||
serializer.read(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
AmbientSound sound;
|
||||
int clip_idx;
|
||||
serializer.read(clip_idx);
|
||||
if (clip_idx >= 0) sound.clip = m_clips[clip_idx + clip_offset];
|
||||
ASSERT(version >= (i32)Version::CLIPS_REWORKED);
|
||||
const char* path = serializer.readString();
|
||||
Clip* res = path[0] ? m_system.getEngine().getResourceManager().load<Clip>(Path(path)) : nullptr;
|
||||
sound.clip = res;
|
||||
serializer.read(sound.entity);
|
||||
sound.entity = entity_map.get(sound.entity);
|
||||
serializer.read(sound.is_3d);
|
||||
|
@ -447,112 +387,22 @@ struct AudioSceneImpl final : AudioScene
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
u32 getClipCount() const override { return m_clips.size(); }
|
||||
|
||||
|
||||
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, const Path& clip, bool is_3d) override {
|
||||
Clip* res = m_system.getEngine().getResourceManager().load<Clip>(clip);
|
||||
return play(entity, res, is_3d);
|
||||
}
|
||||
|
||||
|
||||
void removeClip(ClipInfo* info) override
|
||||
{
|
||||
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;
|
||||
|
||||
SoundHandle play(EntityRef entity, Clip* clip, bool is_3d) {
|
||||
for (PlayingSound& sound : m_playing_sounds) {
|
||||
if (sound.buffer_id == AudioDevice::INVALID_BUFFER_HANDLE) {
|
||||
if (!clip->isReady()) return INVALID_SOUND_HANDLE;
|
||||
|
||||
int flags = is_3d ? (int)AudioDevice::BufferFlags::IS3D : 0;
|
||||
auto buffer = m_device.createBuffer(
|
||||
clip->getData(), clip->getSize(), clip->getChannels(), clip->getSampleRate(), flags);
|
||||
auto buffer = m_device.createBuffer(clip->getData(), clip->getSize(), clip->getChannels(), clip->getSampleRate(), flags);
|
||||
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);
|
||||
m_device.setSourcePosition(buffer, pos);
|
||||
|
@ -560,10 +410,10 @@ struct AudioSceneImpl final : AudioScene
|
|||
sound.is_3d = is_3d;
|
||||
sound.buffer_id = buffer;
|
||||
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 r2 = zone.radius * zone.radius;
|
||||
if (dist2 > r2) continue;
|
||||
|
@ -573,8 +423,7 @@ struct AudioSceneImpl final : AudioScene
|
|||
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();
|
||||
double r2 = zone.radius * zone.radius;
|
||||
if (dist2 > r2) continue;
|
||||
|
@ -596,6 +445,10 @@ struct AudioSceneImpl final : AudioScene
|
|||
ASSERT(sound_id >= 0 && sound_id < (int)lengthOf(m_playing_sounds));
|
||||
m_device.stop(m_playing_sounds[sound_id].buffer_id);
|
||||
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;
|
||||
IAllocator& m_allocator;
|
||||
Universe& m_universe;
|
||||
Array<ClipInfo*> m_clips;
|
||||
AudioSystem& m_system;
|
||||
PlayingSound m_playing_sounds[AudioDevice::MAX_PLAYING_SOUNDS];
|
||||
AnimationScene* m_animation_scene = nullptr;
|
||||
|
|
|
@ -50,43 +50,21 @@ struct AudioScene : IScene
|
|||
using SoundHandle = i32;
|
||||
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,
|
||||
Universe& universe,
|
||||
struct IAllocator& allocator);
|
||||
|
||||
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 ChorusZone& getChorusZone(EntityRef entity) = 0;
|
||||
|
||||
virtual ClipInfo* getAmbientSoundClip(EntityRef entity) = 0;
|
||||
virtual int getAmbientSoundClipIndex(EntityRef entity) = 0;
|
||||
virtual void setAmbientSoundClipIndex(EntityRef entity, int index) = 0;
|
||||
virtual void setAmbientSoundClip(EntityRef entity, ClipInfo* clip) = 0;
|
||||
virtual Path getAmbientSoundClip(EntityRef entity) = 0;
|
||||
virtual void setAmbientSoundClip(EntityRef entity, const Path& clip) = 0;
|
||||
virtual bool isAmbientSound3D(EntityRef entity) = 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, ClipInfo* clip, bool is_3d) = 0;
|
||||
virtual SoundHandle play(EntityRef entity, const Path& clip, bool is_3d) = 0;
|
||||
virtual void stop(SoundHandle sound_id) = 0;
|
||||
virtual void setVolume(SoundHandle sound_id, float volume) = 0;
|
||||
|
||||
|
|
|
@ -12,25 +12,23 @@
|
|||
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)
|
||||
{
|
||||
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;
|
||||
static auto audio_scene = scene("audio",
|
||||
functions(
|
||||
LUMIX_FUNC(AudioScene::setMasterVolume),
|
||||
LUMIX_FUNC(AudioScene::playSound),
|
||||
LUMIX_FUNC(AudioScene::play),
|
||||
LUMIX_FUNC(AudioScene::setVolume),
|
||||
LUMIX_FUNC(AudioScene::setEcho)
|
||||
),
|
||||
component("ambient_sound",
|
||||
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("echo_zone",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "engine/profiler.h"
|
||||
#include "engine/resource.h"
|
||||
#include "engine/string.h"
|
||||
#include "engine/stream.h"
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
#include "stb/stb_vorbis.cpp"
|
||||
|
||||
|
@ -25,8 +26,18 @@ void Clip::unload()
|
|||
bool Clip::load(u64 size, const u8* mem)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
||||
m_data.resize(res * m_channels);
|
||||
|
|
|
@ -13,6 +13,10 @@ namespace Lumix
|
|||
struct Clip final : Resource
|
||||
{
|
||||
public:
|
||||
enum class Format : u8 {
|
||||
OGG
|
||||
};
|
||||
|
||||
Clip(const Path& path, ResourceManager& manager, IAllocator& allocator)
|
||||
: Resource(path, manager, allocator)
|
||||
, m_data(allocator)
|
||||
|
@ -30,6 +34,8 @@ public:
|
|||
float getLengthSeconds() const { return m_data.size() / float(m_channels * m_sample_rate); }
|
||||
|
||||
static const ResourceType TYPE;
|
||||
bool m_looped = false;
|
||||
float m_volume = 1;
|
||||
|
||||
private:
|
||||
int m_channels;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "editor/world_editor.h"
|
||||
#include "engine/crc32.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/lua_wrapper.h"
|
||||
#include "engine/reflection.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)
|
||||
: m_app(app)
|
||||
|
@ -32,6 +33,36 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
|
|||
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)
|
||||
{
|
||||
|
@ -56,6 +87,16 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
|
|||
{
|
||||
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]);
|
||||
ImGuiEx::Label("Length");
|
||||
ImGui::Text("%f", clip->getLengthSeconds());
|
||||
|
@ -63,7 +104,7 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
|
|||
|
||||
if (m_playing_clip >= 0)
|
||||
{
|
||||
if (ImGui::Button("Stop"))
|
||||
if (ImGui::Button(ICON_FA_STOP "Stop"))
|
||||
{
|
||||
stopAudio();
|
||||
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();
|
||||
|
||||
|
@ -85,6 +126,19 @@ struct AssetBrowserPlugin final : AssetBrowser::IPlugin
|
|||
device.play(handle, true);
|
||||
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;
|
||||
StudioApp& m_app;
|
||||
AssetBrowser& m_browser;
|
||||
};
|
||||
|
||||
|
||||
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", ¤t, 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;
|
||||
Meta m_meta;
|
||||
u32 m_meta_res = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -226,7 +169,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
|
|||
explicit StudioAppPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
, m_asset_browser_plugin(app)
|
||||
, m_clip_manager_ui(app)
|
||||
{}
|
||||
|
||||
const char* getName() const override { return "audio"; }
|
||||
|
@ -241,8 +183,8 @@ struct StudioAppPlugin : StudioApp::IPlugin
|
|||
IAllocator& allocator = m_app.getAllocator();
|
||||
|
||||
m_app.getAssetBrowser().addPlugin(m_asset_browser_plugin);
|
||||
|
||||
m_app.addPlugin(m_clip_manager_ui);
|
||||
const char* extensions[] = { "ogg", nullptr };
|
||||
m_app.getAssetCompiler().addPlugin(m_asset_browser_plugin, extensions);
|
||||
}
|
||||
|
||||
|
||||
|
@ -280,13 +222,12 @@ struct StudioAppPlugin : StudioApp::IPlugin
|
|||
~StudioAppPlugin()
|
||||
{
|
||||
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;
|
||||
AssetBrowserPlugin m_asset_browser_plugin;
|
||||
ClipManagerUI m_clip_manager_ui;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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<Vec2>) { return Variant::VEC2; }
|
||||
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; }
|
||||
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 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 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 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; }
|
||||
|
|
Loading…
Reference in a new issue