added audio stuff

This commit is contained in:
2026-01-18 19:42:25 +01:00
parent 5fdcb6989f
commit f9185e7b26
13 changed files with 471 additions and 1 deletions

View File

@@ -0,0 +1,10 @@
#include "AudioListener.hpp"
#include "Vector3.hpp"
#include "elements/Transformable.hpp"
#include "elements/AudioEngine.hpp"
void TSE::AudioListener::OnUpdate()
{
Vector3 pos = baseObject->GetGlobalPosition();
ma_engine_listener_set_position(AudioEngine::engine, 0, pos.x, pos.y, -pos.z);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#define AUDIOLISTENER typeid(AudioListener).name()
#include "elements/BehaviourScript.hpp"
namespace TSE
{
class AudioListener : public BehaviourScript
{
void OnUpdate() override;
inline const char* GetName() override
{
return "Audio Listener";
}
};
} // namespace TSE

View File

@@ -0,0 +1,135 @@
#include "AudioSource.hpp"
#include "Vector3.hpp"
#include "elements/Transformable.hpp"
float TSE::AudioSource::GetMinDistance()
{
return minDistance;
}
float TSE::AudioSource::GetMaxDistance()
{
return maxDistance;
}
bool TSE::AudioSource::GetGlobal()
{
return global;
}
void TSE::AudioSource::SetGlobal(bool v)
{
if(global != v)
{
auto it = sounds.begin();
for (int j = 0; j < sounds.size(); j++)
{
it->second;
ma_sound_set_spatialization_enabled(it->second, !v);
it++;
}
}
global = v;
}
void TSE::AudioSource::SetMinDistance(float v)
{
minDistance = v;
auto it = sounds.begin();
for (int j = 0; j < sounds.size(); j++)
{
it->second;
ma_sound_set_min_distance(it->second, minDistance);
it++;
}
}
void TSE::AudioSource::SetMaxDistance(float v)
{
maxDistance = v;
auto it = sounds.begin();
for (int j = 0; j < sounds.size(); j++)
{
it->second;
ma_sound_set_max_distance(it->second, maxDistance);
it++;
}
}
void TSE::AudioSource::AddClip(AudioClip *clip)
{
clips[clip->name] = clip;
ma_sound* s = clip->GetAudioSound();
ma_sound_set_spatialization_enabled(s, !global);
ma_sound_set_attenuation_model(s, ma_attenuation_model_linear);
ma_sound_set_rolloff(s, 1.0f);
ma_sound_set_min_distance(s, minDistance);
ma_sound_set_max_distance(s, maxDistance);
ma_sound_set_min_gain(s, 0.0f);
ma_sound_set_max_gain(s, 1.0f);
sounds[clip->name] = s;
}
void TSE::AudioSource::RemoveClip(std::string name)
{
if(currentlyPlaying == name) StopPlaying();
clips[name]->DestroyAudioSound(sounds[name]);
clips.erase(name);
sounds.erase(name);
}
void TSE::AudioSource::StartClip(std::string name, bool forceRestart)
{
if(currentlyPlaying != name){
StopPlaying();
}
else if(forceRestart)
{
ma_sound_seek_to_pcm_frame(sounds[name], 0);
}
ma_sound_start(sounds[name]);
currentlyPlaying = name;
}
void TSE::AudioSource::StopPlaying()
{
if(currentlyPlaying == "") return;
ma_sound_stop(sounds[currentlyPlaying]);
ma_sound_seek_to_pcm_frame(sounds[currentlyPlaying], 0);
currentlyPlaying = "";
}
void TSE::AudioSource::PausePlaying()
{
if(currentlyPlaying == "") return;
ma_sound_stop(sounds[currentlyPlaying]);
}
TSE::AudioClip *TSE::AudioSource::GetClipAt(int i)
{
auto it = clips.begin();
for (int j = 0; j < i; j++)
{
it++;
}
return it->second;
}
TSE::AudioSource::~AudioSource()
{
int count = clips.size();
for (int i = 0; i < count; i++)
{
std::string name = GetClipAt(0)->name;
RemoveClip(name);
}
}
void TSE::AudioSource::OnUpdate()
{
if(!global)
{
Vector3 pos = baseObject->GetGlobalPosition();
ma_sound_set_position(sounds[currentlyPlaying], pos.x, pos.y, -pos.z);
}
}

View File

@@ -0,0 +1,50 @@
#pragma once
#define AUDIOSOURCE typeid(AudioSource).name()
#include "Types.hpp"
#include "elements/AudioClip.hpp"
#include "elements/BehaviourScript.hpp"
#include "miniaudio.h"
#include <unordered_map>
namespace TSE
{
class AudioSource : public BehaviourScript
{
public:
std::unordered_map<string, AudioClip*> clips;
string currentlyPlaying = "";
private:
bool global = false;
float minDistance = 5;
float maxDistance = 8;
std::unordered_map<std::string, ma_sound*> sounds;
public:
float GetMinDistance();
float GetMaxDistance();
bool GetGlobal();
void SetGlobal(bool v);
void SetMinDistance(float v);
void SetMaxDistance(float v);
void AddClip(AudioClip* clip);
void RemoveClip(std::string name);
void StartClip(std::string name, bool forceRestart = true);
void StopPlaying();
void PausePlaying();
AudioClip* GetClipAt(int i);
~AudioSource();
void OnUpdate() override;
inline const char* GetName() override
{
return "Audio Source";
}
};
} // namespace TSE

View File

@@ -0,0 +1,70 @@
#include "AudioClip.hpp"
#include "Debug.hpp"
#include <filesystem>
#include "AudioEngine.hpp"
TSE::AudioClip::AudioClip(string path)
{
description.path = path;
}
TSE::AudioClip::AudioClip(byte *stream, size_t size)
{
description.audiostream = stream;
description.dataSize = size;
}
ma_sound *TSE::AudioClip::GetAudioSound()
{
if(description.path != ""){
if(std::filesystem::exists(description.path))
{
ma_result res;
ma_sound* sound = (ma_sound*)malloc(sizeof(*sound));
res = ma_sound_init_from_file(AudioEngine::engine, description.path.c_str(), 0, nullptr, nullptr, sound);
if (res != MA_SUCCESS) {
TSE_WARNING("ma_sound_init_from_file failed: " + std::to_string(res));
delete(sound);
return nullptr;
}
return sound;
}
else
{
TSE_WARNING("Can't create ma_sound because the given path dose not exist: \n" + description.path);
}
}
else if(description.audiostream != nullptr && description.dataSize > 0)
{
ma_result res;
ma_decoder m_decoder;
res = ma_decoder_init_memory(description.audiostream, description.dataSize, nullptr, &m_decoder);
if (res != MA_SUCCESS) {
TSE_WARNING("ma_decoder_init_memory failed: " + std::to_string(res));
return nullptr;
}
ma_sound* sound = (ma_sound*)malloc(sizeof(*sound));
res = ma_sound_init_from_data_source(AudioEngine::engine, &m_decoder, MA_SOUND_FLAG_STREAM, nullptr, sound);
ma_decoder_uninit(&m_decoder);
if (res != MA_SUCCESS) {
TSE_WARNING("ma_sound_init_from_data_source failed: " + std::to_string(res));
delete(sound);
return nullptr;
}
return sound;
}
else
{
TSE_WARNING("Can't create ma_sound because description is not set, or unconfigured.");
}
return nullptr;
}
void TSE::AudioClip::DestroyAudioSound(ma_sound *sound)
{
if (!sound) return;
ma_sound_uninit(sound);
delete sound;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Types.hpp"
#include "miniaudio.h"
namespace TSE
{
struct AudioClipDescription
{
public:
string path = "";
byte* audiostream = nullptr;
size_t dataSize = 0;
};
class AudioClip
{
public:
string name = "";
AudioClipDescription description;
AudioClip(string path);
AudioClip(byte* stream, size_t size);
ma_sound* GetAudioSound();
void DestroyAudioSound(ma_sound* sound);
};
} // namespace TSE

View File

@@ -0,0 +1,22 @@
#include "AudioEngine.hpp"
#include "Debug.hpp"
ma_engine* TSE::AudioEngine::engine = nullptr;
void TSE::AudioEngine::Init()
{
ma_result res;
engine = (ma_engine*)malloc(sizeof(*engine));
res = ma_engine_init(nullptr, engine);
if(res != MA_SUCCESS)
{
TSE_WARNING("Couldn't init audio engine. error code: " + std::to_string(res));
}
}
void TSE::AudioEngine::Destroy()
{
ma_engine_uninit(engine);
delete(engine);
engine = nullptr;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "miniaudio.h"
namespace TSE
{
class AudioEngine
{
public:
static ma_engine* engine;
static void Init();
static void Destroy();
};
} // namespace TSE

2
TSE_Core/src/extern/miniaudio.c vendored Normal file
View File

@@ -0,0 +1,2 @@
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"

View File

@@ -4,6 +4,7 @@
#include "utils/Time.hpp"
#include "version.h"
#include "IInputManager.hpp"
#include "elements/AudioEngine.hpp"
TSE::IWindow* TSE::IWindow::lastWindow = nullptr;
@@ -12,6 +13,7 @@ bool TSE::IWindow::BaseInit() const
LuaStateHandler::InitLuaState();
Debug::Init();
Debug::Log("TSE:" + TSE_VERSION_STRING);
AudioEngine::Init();
return true;
}
@@ -22,6 +24,7 @@ void TSE::IWindow::BaseUpdate() const
TSE::IWindow::~IWindow()
{
AudioEngine::Destroy();
IInputManager::instance()->Delete();
Time::Destroy();
Debug::Close();

View File

@@ -1,6 +1,8 @@
#include "EditorSubsystem.hpp"
#include "BehaviourScriptRegistry.hpp"
#include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp"
TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
{
@@ -21,6 +23,8 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("")
BehaviourScriptRegistry::RegisterBehaviourScript("Renderable", []() -> BehaviourScript* {return new Renderable();});
BehaviourScriptRegistry::RegisterBehaviourScript("Particle System", []() -> BehaviourScript* {return new ParticleSystem();});
BehaviourScriptRegistry::RegisterBehaviourScript("Camera", []() -> BehaviourScript* {return new Camera();});
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Listener", []() -> BehaviourScript* {return new AudioListener();});
BehaviourScriptRegistry::RegisterBehaviourScript("Audio Source", []() -> BehaviourScript* {return new AudioSource();});
#pragma region camerastuff

View File

@@ -191,6 +191,10 @@ namespace TSE::EDITOR
{
Draw((RectBase*)element, debug);
}
else if (name == "Audio Source")
{
Draw((AudioSource*)element, debug);
}
else if (name == "Camera")
{
Draw((Camera*)element, debug);
@@ -478,6 +482,78 @@ namespace TSE::EDITOR
if(small) DrawImageAnimationSetCompact(element, debug, label);
else DrawImageAnimationSetNormal(element, debug, label);
}
void ElementDrawer::Draw(AudioSource *element, const bool &debug)
{
float availWidth = ImGui::GetContentRegionAvail().x;
int buttonCount = 3;
float spacing = ImGui::GetStyle().ItemSpacing.x;
float buttonWidth = (availWidth - (buttonCount - 1) * spacing) / buttonCount;
float buttonHeight = 0.0f; // 0.0 = default height
if (ImGui::Button("Play", ImVec2(buttonWidth, buttonHeight)))
{
element->StartClip(element->currentlyPlaying, false);
}
ImGui::SameLine();
if (ImGui::Button("Pause", ImVec2(buttonWidth, buttonHeight)))
{
element->PausePlaying();
}
ImGui::SameLine();
if (ImGui::Button("Stop", ImVec2(buttonWidth, buttonHeight)))
{
element->StopPlaying();
}
ImGui::Separator();
bool global = element->GetGlobal();
if(ImGui::Checkbox("Global", &global))
{
element->SetGlobal(global);
}
float minDistance = element->GetMinDistance();
if(ImGui::DragFloat("Cutoff Distance", &minDistance))
{
element->SetMinDistance(minDistance);
}
float maxDistance = element->GetMaxDistance();
if(ImGui::DragFloat("Falloff Distance", &maxDistance))
{
element->SetMaxDistance(maxDistance);
}
BeginList("Audio Clips");
//if(ListAddBtn()) { } // --> no add function for now maybe when asset manager is done
for(int i = 0; i < element->clips.size(); i++)
{
BeginListItem("subcomponent##" + std::to_string(i));
Draw(element->GetClipAt(i), debug, "", true);
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows))
{
// Check auf Doppelclick mit linker Maustaste
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
element->StopPlaying();
element->currentlyPlaying = element->GetClipAt(i)->name;
}
}
EndListItem();
if(ListItemXBotton("x##" + std::to_string(i)))
{
element->RemoveClip(element->GetClipAt(i)->name);
}
}
EndList();
}
void ElementDrawer::Draw(AudioClip *element, const bool &debug, const std::string &label, const bool small)
{
if(element == nullptr)
{
ImGui::Text("No Audio Clip Asigned");
return;
}
if(small) DrawAudioClipCompact(element, debug, label);
else DrawAudioClipNormal(element, debug, label);
}
void ElementDrawer::Draw(Camera *element, const bool &debug)
{
const bool isMain = (Camera::mainCamera == element);
@@ -737,6 +813,39 @@ namespace TSE::EDITOR
ImGui::Unindent(20.0f);
}
}
void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
float available_width = ImGui::GetContentRegionAvail().x;
float field_width = available_width - label_size.x - item_spacing;
ImVec2 field_size = ImVec2(field_width, label_size.y + 4);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_FrameBg)); // gleiche Farbe wie InputText
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ImGui::GetStyle().FrameRounding);
ImGui::BeginChild("##FakeInput", field_size, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPos(ImVec2(2, 2));
ImGui::TextUnformatted(element->name.c_str());
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SameLine();
ImVec2 cursorCurrent = ImGui::GetCursorPos();
cursorCurrent.y += 2;
ImGui::SetCursorPos(cursorCurrent);
ImGui::TextUnformatted(label.c_str());
}
void ElementDrawer::DrawAudioClipNormal(AudioClip *element, const bool &debug, const std::string &label)
{
//TODO implement
}
void ElementDrawer::DrawImageAnimationSetCompact(ImageAnimationSet *element, const bool &debug, const std::string &label)
{
float item_spacing = ImGui::GetStyle().ItemSpacing.x;

View File

@@ -11,6 +11,8 @@
#include "BehaviourScripts/ImageAnimation.hpp"
#include "BehaviourScripts/RectBase.hpp"
#include "BehaviourScripts/ParticleSystem.hpp"
#include "BehaviourScripts/AudioListener.hpp"
#include "BehaviourScripts/AudioSource.hpp"
namespace TSE::EDITOR
{
@@ -73,10 +75,13 @@ namespace TSE::EDITOR
static void Draw(Sprite* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(Mesh* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(ImageAnimationSet* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(AudioSource* element, const bool& debug);
static void Draw(AudioClip* element, const bool& debug, const std::string& label = "", const bool small = false);
static void Draw(Camera* element, const bool& debug);
static void Draw(ParticleSystem* element, const bool& debug);
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);
static void DrawImageAnimationSetCompact(ImageAnimationSet* element, const bool& debug, const std::string& label);
static void DrawImageAnimationSetNormal(ImageAnimationSet* element, const bool& debug, const std::string& label);
static void DrawMeshCompact(Mesh* element, const bool& debug, const std::string& label);