LumixEngine/src/audio/audio_system.cpp
2016-04-22 20:15:22 +03:00

352 lines
8.2 KiB
C++

#include "audio_system.h"
#include "audio_device.h"
#include "audio_scene.h"
#include "clip_manager.h"
#include "engine/core/crc32.h"
#include "engine/core/path.h"
#include "engine/core/resource_manager.h"
#include "editor/asset_browser.h"
#include "editor/imgui/imgui.h"
#include "editor/studio_app.h"
#include "editor/utils.h"
#include "editor/world_editor.h"
#include "engine/engine.h"
#include "engine/iplugin.h"
#include "engine/plugin_manager.h"
#include "engine/property_register.h"
#include "engine/property_descriptor.h"
#include "renderer/render_scene.h"
static const Lumix::uint32 CLIP_HASH = Lumix::crc32("CLIP");
namespace Lumix
{
static void registerProperties(Lumix::IAllocator& allocator)
{
PropertyRegister::registerComponentType("ambient_sound", "Ambient sound");
PropertyRegister::registerComponentType("audio_listener", "Audio listener");
PropertyRegister::registerComponentType("echo_zone", "Echo zone");
PropertyRegister::add("ambient_sound",
LUMIX_NEW(allocator, EnumPropertyDescriptor<AudioScene>)("Sound",
&AudioScene::getAmbientSoundClipIndex,
&AudioScene::setAmbientSoundClipIndex,
&AudioScene::getClipCount,
&AudioScene::getClipName,
allocator));
PropertyRegister::add("ambient_sound",
LUMIX_NEW(allocator, BoolPropertyDescriptor<AudioScene>)("3D",
&AudioScene::isAmbientSound3D,
&AudioScene::setAmbientSound3D,
allocator));
PropertyRegister::add("echo_zone",
LUMIX_NEW(allocator, DecimalPropertyDescriptor<AudioScene>)("Radius",
&AudioScene::getEchoZoneRadius,
&AudioScene::setEchoZoneRadius,
0.01f,
FLT_MAX,
0.1f,
allocator));
PropertyRegister::add("echo_zone",
LUMIX_NEW(allocator, DecimalPropertyDescriptor<AudioScene>)("Delay (ms)",
&AudioScene::getEchoZoneDelay,
&AudioScene::setEchoZoneDelay,
0.01f,
FLT_MAX,
100.0f,
allocator));
}
struct AudioSystemImpl : public AudioSystem
{
explicit AudioSystemImpl(Engine& engine)
: m_engine(engine)
, m_manager(engine.getAllocator())
, m_device(nullptr)
{
registerProperties(engine.getAllocator());
AudioScene::registerLuaAPI(m_engine.getState());
}
Engine& getEngine() override { return m_engine; }
AudioDevice& getDevice() override { return *m_device; }
ClipManager& getClipManager() override { return m_manager; }
bool create() override
{
m_device = AudioDevice::create(m_engine);
if (!m_device) return false;
m_manager.create(CLIP_HASH, m_engine.getResourceManager());
return true;
}
void destroy() override
{
AudioDevice::destroy(*m_device);
m_manager.destroy();
}
const char* getName() const override { return "audio"; }
IScene* createScene(Universe& ctx)
{
return AudioScene::createInstance(*this, ctx, m_engine.getAllocator());
}
void destroyScene(IScene* scene) { AudioScene::destroyInstance(static_cast<AudioScene*>(scene)); }
ClipManager m_manager;
Engine& m_engine;
AudioDevice* m_device;
};
namespace {
struct AssetBrowserPlugin : public AssetBrowser::IPlugin
{
explicit AssetBrowserPlugin(StudioApp& app)
: m_app(app)
, m_browser(*app.getAssetBrowser())
, m_playing_clip(-1)
{
}
Lumix::AudioDevice& getAudioDevice(Lumix::Engine& engine)
{
auto* audio = static_cast<Lumix::AudioSystem*>(engine.getPluginManager().getPlugin("audio"));
return audio->getDevice();
}
void stopAudio()
{
if (m_playing_clip < 0) return;
getAudioDevice(m_app.getWorldEditor()->getEngine()).stop(m_playing_clip);
m_playing_clip = -1;
}
const char* getName() const override
{
return "Audio";
}
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
{
if (type != CLIP_HASH) return false;
auto* clip = static_cast<Lumix::Clip*>(resource);
ImGui::LabelText("Length", "%f", clip->getLengthSeconds());
auto& device = getAudioDevice(m_app.getWorldEditor()->getEngine());
if (m_playing_clip >= 0)
{
if (ImGui::Button("Stop"))
{
stopAudio();
return true;
}
float time = device.getCurrentTime(m_playing_clip);
if (ImGui::SliderFloat("Time", &time, 0, clip->getLengthSeconds(), "%.2fs"))
{
device.setCurrentTime(m_playing_clip, time);
}
}
if (m_playing_clip < 0 && ImGui::Button("Play"))
{
stopAudio();
auto handle = device.createBuffer(clip->getData(),
clip->getSize(),
clip->getChannels(),
clip->getSampleRate(),
0);
device.play(handle, false);
m_playing_clip = handle;
}
return true;
}
void onResourceUnloaded(Lumix::Resource*) override
{
stopAudio();
}
bool hasResourceManager(Lumix::uint32 type) const override
{
return type == CLIP_HASH;
}
Lumix::uint32 getResourceType(const char* ext) override
{
if (Lumix::compareString(ext, "ogg") == 0) return CLIP_HASH;
return 0;
}
int m_playing_clip;
StudioApp& m_app;
AssetBrowser& m_browser;
};
struct StudioAppPlugin : public StudioApp::IPlugin
{
explicit StudioAppPlugin(StudioApp& app)
: m_app(app)
{
m_filter[0] = 0;
m_is_opened = false;
m_action = LUMIX_NEW(app.getWorldEditor()->getAllocator(), Action)("Clip manager", "clip_manager");
m_action->func.bind<StudioAppPlugin, &StudioAppPlugin::onAction>(this);
}
void onAction()
{
m_is_opened = !m_is_opened;
}
void onWindowGUI() override
{
if (ImGui::BeginDock("Clip Manager", &m_is_opened))
{
ImGui::InputText("Filter", m_filter, Lumix::lengthOf(m_filter));
auto* audio_scene =
static_cast<Lumix::AudioScene*>(m_app.getWorldEditor()->getScene(Lumix::crc32("audio")));
int clip_count = audio_scene->getClipCount();
for (int clip_id = 0; clip_id < clip_count; ++clip_id)
{
auto* clip_info = audio_scene->getClipInfo(clip_id);
if (m_filter[0] != 0 && Lumix::stristr(clip_info->name, m_filter) == 0)
{
continue;
}
if (ImGui::TreeNode((const void*)(intptr_t)clip_id, clip_info->name))
{
char buf[30];
Lumix::copyString(buf, Lumix::lengthOf(buf), clip_info->name);
if (ImGui::InputText("Name", buf, sizeof(buf)))
{
Lumix::copyString(clip_info->name, buf);
clip_info->name_hash = Lumix::crc32(buf);
}
auto* clip = audio_scene->getClipInfo(clip_id)->clip;
char path[Lumix::MAX_PATH_LENGTH];
Lumix::copyString(path, clip ? clip->getPath().c_str() : "");
if (m_app.getAssetBrowser()->resourceInput(
"Clip", "", path, Lumix::lengthOf(path), CLIP_HASH))
{
audio_scene->setClip(clip_id, Lumix::Path(path));
}
bool looped = audio_scene->getClipInfo(clip_id)->looped;
if (ImGui::Checkbox("Looped", &looped))
{
clip_info->looped = looped;
}
if (ImGui::Button("Remove"))
{
audio_scene->removeClip(clip_info);
--clip_count;
}
ImGui::TreePop();
}
}
if (ImGui::Button("Add"))
{
audio_scene->addClip("test", Lumix::Path("test.ogg"));
}
}
ImGui::EndDock();
}
StudioApp& m_app;
char m_filter[256];
bool m_is_opened;
};
struct EditorPlugin : public WorldEditor::Plugin
{
explicit EditorPlugin(WorldEditor& editor)
: m_editor(editor)
{
}
bool showGizmo(ComponentUID cmp) override
{
static const uint32 ECHO_ZONE_HASH = crc32("echo_zone");
if (cmp.type == ECHO_ZONE_HASH)
{
auto* audio_scene = static_cast<AudioScene*>(cmp.scene);
float radius = audio_scene->getEchoZoneRadius(cmp.index);
Universe& universe = audio_scene->getUniverse();
Vec3 pos = universe.getPosition(cmp.entity);
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
if (!scene) return true;
scene->addDebugSphere(pos, radius, 0xff0000ff, 0);
return true;
}
return false;
}
WorldEditor& m_editor;
};
} // anonymous namespace
LUMIX_STUDIO_ENTRY(audio)
{
auto& editor = *app.getWorldEditor();
auto& allocator = editor.getAllocator();
auto* asset_browser_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
app.getAssetBrowser()->addPlugin(*asset_browser_plugin);
auto* app_plugin = LUMIX_NEW(allocator, StudioAppPlugin)(app);
app.addPlugin(*app_plugin);
auto* plugin = LUMIX_NEW(allocator, EditorPlugin)(editor);
editor.addPlugin(*plugin);
}
LUMIX_PLUGIN_ENTRY(audio)
{
return LUMIX_NEW(engine.getAllocator(), Lumix::AudioSystemImpl)(engine);
}
} // namespace Lumix