diff --git a/CMakeLists.txt b/CMakeLists.txt index c414dbb..d49f34a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,7 @@ target_link_libraries(${PROJECT_NAME} zstd::zstd OpenAL::OpenAL harfbuzz::harfbuzz + Opus::Opus $<$:ws2_32> ) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 7ff0006..12ba850 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -9,7 +9,7 @@ find_package(OpenAL REQUIRED) find_package(harfbuzz REQUIRED) find_package(Freetype REQUIRED) find_package(SDL3 REQUIRED) - +find_package(Opus REQUIRED) # Third-party libraries FetchContent_Declare( diff --git a/cmake/modules/FindOpus.cmake b/cmake/modules/FindOpus.cmake new file mode 100644 index 0000000..fa05ea2 --- /dev/null +++ b/cmake/modules/FindOpus.cmake @@ -0,0 +1,25 @@ +include(FindPackageHandleStandardArgs) + +find_path(OPUS_INCLUDE_DIR + NAMES opus/opus.h opus.h +) + +find_library(OPUS_LIBRARY + NAMES opus +) + +find_package_handle_standard_args( + Opus + REQUIRED_VARS + OPUS_INCLUDE_DIR + OPUS_LIBRARY +) + +if(Opus_FOUND) + add_library(Opus::Opus UNKNOWN IMPORTED) + + set_target_properties(Opus::Opus PROPERTIES + IMPORTED_LOCATION "${OPUS_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${OPUS_INCLUDE_DIR}" + ) +endif() \ No newline at end of file diff --git a/include/Cubed/audio/audio_engine.hpp b/include/Cubed/audio/audio_engine.hpp index c800800..455af16 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/audio/audio_fade.hpp" +#include "Cubed/audio/audio_recording.hpp" #include "Cubed/audio/audio_source.hpp" #include "Cubed/audio/sound_manager.hpp" #include "Cubed/audio/source_pool.hpp" @@ -10,13 +11,15 @@ #include #include #include +#include #include #include namespace Cubed { -class ClientWorld; +class NetworkClient; class AudioEngine { + public: AudioEngine(Config& config); AudioEngine(const AudioEngine&) = delete; @@ -41,11 +44,22 @@ public: float& bgm_target_volume(); + void set_client(std::weak_ptr client); + + void send_voice(const std::array&); + void receive_voice(std::span opus, const glm::vec3& pos); + + AudioRecording& audio_recording(); + private: using FadeMap = std::unordered_map; bool m_init{false}; ALCdevice* device{nullptr}; ALCcontext* context{nullptr}; + OpusEncoder* m_encoder{nullptr}; + OpusDecoder* m_decoder{nullptr}; + AudioRecording m_recording; + std::weak_ptr m_client; glm::vec3 listener_pos; std::unique_ptr m_bgm; FadeMap m_fade_map; diff --git a/include/Cubed/audio/audio_recording.hpp b/include/Cubed/audio/audio_recording.hpp new file mode 100644 index 0000000..b5936ac --- /dev/null +++ b/include/Cubed/audio/audio_recording.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +namespace Cubed { +class AudioEngine; +class AudioRecording { +public: + static constexpr int SAMPLE_RATE = 48000; + static constexpr int FRAME_MS = 20; + static constexpr int FRAME_SAMPLES = SAMPLE_RATE * FRAME_MS / 1000; + AudioRecording(AudioEngine& engine); + AudioRecording(const AudioRecording&) = delete; + AudioRecording(AudioRecording&&) = delete; + AudioRecording& operator=(const AudioRecording&) = delete; + AudioRecording& operator=(AudioRecording&&) = delete; + ~AudioRecording(); + + void update(); + + void init(); + void start(); + void stop(); + bool is_recording() const; + +private: + AudioEngine& m_engine; + ALCdevice* m_capture = nullptr; + bool m_recording = false; + void send_voice(const std::array& pcm); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_source.hpp b/include/Cubed/audio/audio_source.hpp index 3c798ef..b4a09de 100644 --- a/include/Cubed/audio/audio_source.hpp +++ b/include/Cubed/audio/audio_source.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace Cubed { enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED }; @@ -21,8 +22,9 @@ public: void set_pitch(float pitch); void play(); void play_2d(const AudioBuffer& buffer); + void play_2d(std::unique_ptr buffer); void play_3d(const AudioBuffer& buffer, const glm::vec3& pos); - + void play_3d(std::unique_ptr buffer, const glm::vec3& pos); void stop(); void pause(); float duration() const; @@ -42,6 +44,8 @@ public: void clear_effect_slot(); private: + std::unique_ptr m_buffer; + ALuint m_source = 0; float m_target_volume = 1.0f; float m_duration = 0.0f; diff --git a/include/Cubed/audio/source_pool.hpp b/include/Cubed/audio/source_pool.hpp index 577f086..ff543b0 100644 --- a/include/Cubed/audio/source_pool.hpp +++ b/include/Cubed/audio/source_pool.hpp @@ -4,7 +4,7 @@ namespace Cubed { class SourcePool { private: - std::vector m_sources; + std::vector> m_sources; public: explicit SourcePool(size_t size); @@ -15,9 +15,10 @@ public: SourcePool& operator=(SourcePool&&) = delete; void update(); + [[nodiscard]] AudioSource* acquire(); - std::vector& sources(); + std::vector>& sources(); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 869f6b8..158a26d 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -106,7 +106,7 @@ public: void receive_chat_message(ChatMsg& msg); void send_chat_message(ChatMessage& message); - + void receive_voice_message(VoiceMsg& msg); template void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) { m_ticktimers.emplace( @@ -115,6 +115,11 @@ public: } private: + struct VoiceMessage { + std::string data; + glm::vec3 pos; + }; + std::atomic m_is_pending_delete_queue_free{false}; std::mutex m_delete_vbo_mutex; std::mutex m_delete_vao_mutex; @@ -153,6 +158,7 @@ private: tbb::concurrent_queue m_dirty_chunk_queue; tbb::concurrent_queue m_pending_sound; tbb::concurrent_queue m_message_queue; + tbb::concurrent_queue m_voice_queue; std::deque m_dirty_queue; std::vector m_render_snapshots; diff --git a/include/Cubed/gameplay/network_client.hpp b/include/Cubed/gameplay/network_client.hpp index a59bcd3..ae569dd 100644 --- a/include/Cubed/gameplay/network_client.hpp +++ b/include/Cubed/gameplay/network_client.hpp @@ -21,6 +21,7 @@ public: bool is_connect_error() const; std::string get_error_string() const; void clear_error(); + ClientWorld& world(); private: struct Task { diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index 2769bc0..1af675a 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -62,6 +62,7 @@ enum class PacketEnum : uint16_t { S2C_CLEAR_ALL_CHUNKS = 3005, UPDATE_TIME = 3006, CHAT_MSG = 4001, + VOICE_MSG = 4002, PING = 9001, PONG = 9002 @@ -125,6 +126,9 @@ template <> constexpr uint16_t get_packet_id() { template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::CHAT_MSG); } +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::VOICE_MSG); +} template requires std::derived_from diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index e61bc4e..971e42c 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -81,7 +81,7 @@ public: void handle_block_change(const BlockChangeReq& req); void handle_chat_message(ChatMsg& msg); - + void handle_voice_message(VoiceMsg& msg); int chunk_size() const; template void register_timer(std::string_view id, TickType threshold, Fn&& f) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 57ca975..2d49aaf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,4 +86,5 @@ target_sources(${PROJECT_NAME} localization.cpp tools/system_locate.cpp ui/chat_box.cpp + audio/audio_recording.cpp ) \ No newline at end of file diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index b5c3994..92f3a4b 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -1,13 +1,19 @@ #include "Cubed/audio/audio_engine.hpp" #include "Cubed/audio/audio_error.hpp" +#include "Cubed/gameplay/client_world.hpp" +#include "Cubed/gameplay/network_client.hpp" #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/log.hpp" #include - +using namespace google::protobuf; +namespace { +constexpr std::size_t OPUS_MAX_PACKET_SIZE = 400; +} namespace Cubed { -AudioEngine::AudioEngine(Config& config) : m_config(config) {}; +AudioEngine::AudioEngine(Config& config) + : m_recording(*this), m_config(config) {}; AudioEngine::~AudioEngine() { if (!m_init) { @@ -21,13 +27,35 @@ AudioEngine::~AudioEngine() { m_low_pass_filter.reset(); m_underwater_effect.reset(); m_underwater_slot.reset(); - + opus_encoder_destroy(m_encoder); + opus_decoder_destroy(m_decoder); alcMakeContextCurrent(nullptr); alcDestroyContext(context); alcCloseDevice(device); } void AudioEngine::init() { + { + int error; + m_encoder = opus_encoder_create(AudioRecording::SAMPLE_RATE, + 1, // Mono + OPUS_APPLICATION_VOIP, &error); + + if (error != OPUS_OK) { + Logger::error("Can't Create Opus Encoder Error {}", error); + } + opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(24000)); + opus_encoder_ctl(m_encoder, OPUS_SET_COMPLEXITY(5)); + opus_encoder_ctl(m_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + } + { + int error; + m_decoder = opus_decoder_create(AudioRecording::SAMPLE_RATE, 1, &error); + + if (error != OPUS_OK) { + Logger::error("Can't Create Opus Encoder Error {}", error); + } + } device = alcOpenDevice(NULL); if (!device) { throw std::runtime_error("Failed to open OpenAL device."); @@ -62,7 +90,7 @@ void AudioEngine::init() { alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); check_al_error(); - + m_recording.init(); m_music_volume = m_config.get("volume.music", 1.0f); m_sfx_volume = m_config.get("volume.SFX", 1.0f); @@ -144,10 +172,10 @@ void AudioEngine::play_2d(const std::string& sound, bool check) { return; } auto* source = m_pool->acquire(); - source->set_volume(m_sfx_volume); if (!source) { Logger::error("Source is Full"); } + source->set_volume(m_sfx_volume); try { auto& buffer = m_sounds.get_buffer(sound); @@ -181,6 +209,7 @@ void AudioEngine::update() { fade.update(); } m_pool->update(); + m_recording.update(); } void AudioEngine::reload_config() { @@ -201,11 +230,11 @@ void AudioEngine::underwater_change(bool underwater) { } for (auto& source : m_pool->sources()) { if (m_underwater) { - source.set_filter(*m_low_pass_filter); - source.set_effect_slot(*m_underwater_slot); + source->set_filter(*m_low_pass_filter); + source->set_effect_slot(*m_underwater_slot); } else { - source.clear_filter(); - source.clear_effect_slot(); + source->clear_filter(); + source->clear_effect_slot(); } } if (underwater) { @@ -219,4 +248,87 @@ void AudioEngine::underwater_change(bool underwater) { float& AudioEngine::bgm_target_volume() { return m_bgm->target_volume(); } +void AudioEngine::set_client(std::weak_ptr client) { + m_client = client; +} + +void AudioEngine::send_voice( + const std::array& pcm) { + { + AudioData data; + data.pcm = {pcm.begin(), pcm.end()}; + data.channels = 1; + data.sample_rate = AudioRecording::SAMPLE_RATE; + auto* source = m_pool->acquire(); + source->set_volume(m_sfx_volume); + if (!source) { + Logger::error("Source is Full"); + return; + } + std::unique_ptr buffer = + std::make_unique(data); + + if (m_efx_supported && m_underwater) { + source->set_filter(*m_low_pass_filter); + source->set_effect_slot(*m_underwater_slot); + } + source->play_2d(std::move(buffer)); + } + /* + std::array opus; + int len = opus_encode(m_encoder, pcm.data(), AudioRecording::FRAME_SAMPLES, + opus.data(), opus.size()); + + if (len < 0) { + Logger::error("Opus encode failed: {}", opus_strerror(len)); + return; + } + Logger::info("opus encode len={}", len); + if (auto c = m_client.lock()) { + Arena arena; + auto msg = Arena::Create(&arena); + msg->set_uuid(c->world().get_player().get_uuid()); + msg->set_opus_data(reinterpret_cast(opus.data()), len); + auto* pos = msg->mutable_pos(); + auto p = c->world().get_player().get_player_pos(); + pos->set_x(p.x); + pos->set_y(p.y); + pos->set_z(p.z); + c->send(make_packet(*msg)); + } + */ +} +void AudioEngine::receive_voice(std::span opus, const glm::vec3& pos) { + AudioData data; + data.pcm.resize(AudioRecording::FRAME_SAMPLES); + data.channels = 1; + data.sample_rate = AudioRecording::SAMPLE_RATE; + int len = opus_decode( + m_decoder, reinterpret_cast(opus.data()), opus.size(), + data.pcm.data(), AudioRecording::FRAME_SAMPLES, 0); + if (len < 0) { + Logger::error("Opus decode failed: {}", opus_strerror(len)); + return; + } + Logger::info("decode samples={}", len); + Logger::info("Receive Vocie and start play"); + auto* source = m_pool->acquire(); + source->set_volume(m_sfx_volume); + if (!source) { + Logger::error("Source is Full"); + return; + } + for (int i = 0; i < 10; ++i) + Logger::info("recv {}", data.pcm[i]); + std::unique_ptr buffer = std::make_unique(data); + + if (m_efx_supported && m_underwater) { + source->set_filter(*m_low_pass_filter); + source->set_effect_slot(*m_underwater_slot); + } + source->play_3d(std::move(buffer), pos); +} + +AudioRecording& AudioEngine::audio_recording() { return m_recording; } + } // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_recording.cpp b/src/audio/audio_recording.cpp new file mode 100644 index 0000000..626149d --- /dev/null +++ b/src/audio/audio_recording.cpp @@ -0,0 +1,74 @@ +#include "Cubed/audio/audio_recording.hpp" + +#include "Cubed/audio/audio_engine.hpp" +#include "Cubed/tools/log.hpp" + +#include + +namespace Cubed { +AudioRecording::AudioRecording(AudioEngine& engine) + : m_engine(engine) { + + }; +AudioRecording::~AudioRecording() { + if (m_capture) { + if (m_recording) { + stop(); + } + + alcCaptureCloseDevice(m_capture); + } +} + +void AudioRecording::init() { + m_capture = + alcCaptureOpenDevice(nullptr, SAMPLE_RATE, AL_FORMAT_MONO16, 4096); + if (m_capture) { + Logger::info("Open Audio Capture Success"); + } else { + Logger::error("Fail to Open Audio Capture Device"); + } + ALCint freq = 0; + alcGetIntegerv(m_capture, ALC_FREQUENCY, 1, &freq); + + Logger::info("Capture frequency={}", freq); +} + +void AudioRecording::start() { + ALCint samples; + + alcGetIntegerv(m_capture, ALC_CAPTURE_SAMPLES, 1, &samples); + + std::vector dump(samples); + alcCaptureSamples(m_capture, dump.data(), samples); + alcCaptureStart(m_capture); + m_recording = true; +} + +void AudioRecording::stop() { + alcCaptureStop(m_capture); + m_recording = false; +} +bool AudioRecording::is_recording() const { return m_recording; } +void AudioRecording::update() { + if (!m_recording || !m_capture) { + return; + } + + ALCint available = 0; + alcGetIntegerv(m_capture, ALC_CAPTURE_SAMPLES, 1, &available); + while (available >= FRAME_SAMPLES) { + std::array pcm; + alcCaptureSamples(m_capture, pcm.data(), FRAME_SAMPLES); + for (int i = 0; i < 20; i++) { + Logger::info("pcm[{}]={}", i, pcm[i]); + } + send_voice(pcm); + available -= FRAME_SAMPLES; + } +} +void AudioRecording::send_voice(const std::array& pcm) { + + m_engine.send_voice(pcm); +} +} // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_source.cpp b/src/audio/audio_source.cpp index 30b9562..9bb83b8 100644 --- a/src/audio/audio_source.cpp +++ b/src/audio/audio_source.cpp @@ -91,12 +91,19 @@ void AudioSource::play_2d(const AudioBuffer& buffer) { set_buffer_2d(buffer); play(); } - +void AudioSource::play_2d(std::unique_ptr buffer) { + m_buffer = std::move(buffer); + play_2d(*m_buffer); +} void AudioSource::play_3d(const AudioBuffer& buffer, const glm::vec3& pos) { set_buffer_3d(buffer, pos); play(); } - +void AudioSource::play_3d(std::unique_ptr buffer, + const glm::vec3& pos) { + m_buffer = std::move(buffer); + play_3d(*m_buffer, pos); +} void AudioSource::stop() { alSourceStop(m_source); } void AudioSource::pause() { alSourcePause(m_source); } @@ -163,7 +170,7 @@ void AudioSource::reset() { clear_filter(); clear_effect_slot(); - + m_buffer.reset(); m_duration = 0.0f; m_target_volume = 1.0f; m_using = false; diff --git a/src/audio/source_pool.cpp b/src/audio/source_pool.cpp index c266a03..08f2707 100644 --- a/src/audio/source_pool.cpp +++ b/src/audio/source_pool.cpp @@ -1,24 +1,32 @@ #include "Cubed/audio/source_pool.hpp" namespace Cubed { -SourcePool::SourcePool(size_t size) { m_sources.resize(size); } +SourcePool::SourcePool(size_t size) { + m_sources.reserve(size); + for (size_t i = 0; i < size; ++i) { + m_sources.emplace_back(std::make_unique()); + } +} SourcePool::~SourcePool() { m_sources.clear(); } void SourcePool::update() { - for (auto& source : m_sources) { - if (source.in_use() && source.state() == AudioState::STOPPED) { - source.reset(); + for (auto& source_ptr : m_sources) { + if (source_ptr->in_use() && + source_ptr->state() == AudioState::STOPPED) { + source_ptr->reset(); } } } AudioSource* SourcePool::acquire() { - for (auto& source : m_sources) { - if (!source.in_use()) { - source.mark_in_use(); - return &source; + for (auto& source_ptr : m_sources) { + if (!source_ptr->in_use()) { + source_ptr->mark_in_use(); + return source_ptr.get(); } } return nullptr; } -std::vector& SourcePool::sources() { return m_sources; } +std::vector>& SourcePool::sources() { + return m_sources; +} } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index 24f38dd..6bfc17c 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -754,6 +754,13 @@ void ClientWorld::request_exit() { void ClientWorld::receive_chat_message(ChatMsg& msg) { m_message_queue.emplace(msg.name(), msg.msg(), Tools::get_time_ticks()); } + +void ClientWorld::receive_voice_message(VoiceMsg& msg) { + glm::vec3 pos{msg.pos().x(), msg.pos().y(), msg.pos().z()}; + Logger::info("Receive Voice From net"); + m_voice_queue.emplace(msg.opus_data(), pos); +} + void ClientWorld::send_chat_message(ChatMessage& message) { Arena arena; auto msg = Arena::Create(&arena); @@ -932,6 +939,11 @@ void ClientWorld::update(float delta_time) { while (m_message_queue.try_pop(message)) { m_world_scene.handle_chat_message(message); } + + VoiceMessage vm; + while (m_voice_queue.try_pop(vm)) { + m_audio.receive_voice(vm.data, vm.pos); + } } bool ClientWorld::handle_event(const Event& e) { diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 1d6956e..125edfa 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -141,6 +141,12 @@ asio::awaitable NetworkClient::read_loop() { m_world.receive_chat_message(*msg); } } break; + case std::to_underlying(PacketEnum::VOICE_MSG): { + auto* msg = Arena::Create(&arena); + if (decode_packet(*msg, body_data, header)) { + m_world.receive_voice_message(*msg); + } + } } } } catch (const asio::system_error& e) { @@ -228,5 +234,5 @@ void NetworkClient::set_error(std::string_view error) { m_error_string = error; m_connect_error = true; } - +ClientWorld& NetworkClient::world() { return m_world; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 3aed224..00f5ef3 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -763,6 +763,44 @@ void ServerWorld::handle_chat_message(ChatMsg& msg) { }); } +void ServerWorld::handle_voice_message(VoiceMsg& msg) { + Logger::info("Get voice Message"); + auto pool = m_net_thread_pool.load(); + std::string uuid = msg.uuid(); + std::string data = msg.opus_data(); + auto pos = msg.pos(); + glm::vec3 p{pos.x(), pos.y(), pos.z()}; + pool->enqueue([this, uuid = std::move(uuid), data = std::move(data), p]() { + std::vector> session; + + { + std::shared_lock lock(m_player_mutex); + for (auto& [key, player] : m_players) { + if (key == uuid) { + continue; + } + session.emplace_back(player.get_session()); + } + } + + Arena arena; + auto msg = Arena::Create(&arena); + msg->set_uuid(uuid); + + msg->set_opus_data(data); + + auto pos = msg->mutable_pos(); + pos->set_x(p.x); + pos->set_y(p.y); + pos->set_z(p.z); + + for (auto& s : session) { + s->send(make_packet(*msg), 5); + } + Logger::info("Send voice message sum {}", session.size()); + }); +} + void ServerWorld::handle_block_change(const BlockChangeReq& req) { float x = std::floor(req.pos().x()); float y = std::floor(req.pos().y()); diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index 9d02ebd..615b794 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -105,6 +105,12 @@ asio::awaitable Session::read_loop() { m_server_world.handle_chat_message(*msg); } } + if (cmd_id == std::to_underlying(PacketEnum::VOICE_MSG)) { + auto* msg = Arena::Create(&arena); + if (decode_packet(*msg, body_data, header)) { + m_server_world.handle_voice_message(*msg); + } + } } } catch (const asio::system_error& e) { auto ec = e.code(); diff --git a/src/proto/chat/chat.proto b/src/proto/chat/chat.proto index 0b3f064..cfbb2c8 100644 --- a/src/proto/chat/chat.proto +++ b/src/proto/chat/chat.proto @@ -1,6 +1,12 @@ syntax = "proto3"; - +import "common/vector3.proto"; message ChatMsg { string name = 1; string msg = 2; +} + +message VoiceMsg { + string uuid = 1; + Vec3 pos = 2; + bytes opus_data = 3; } \ No newline at end of file diff --git a/src/scene/world_scene.cpp b/src/scene/world_scene.cpp index a65f51e..e916e22 100644 --- a/src/scene/world_scene.cpp +++ b/src/scene/world_scene.cpp @@ -141,7 +141,7 @@ void WorldScene::on_enter() { m_dev_panel.init(); m_pasue_menu.init(); m_hud_ui.init(); - + m_scene_manager.app().audio().set_client(m_client); m_scene_manager.app().window().set_game_running(true); } catch (const std::exception& e) { m_error_ui.set_error(e.what()); @@ -290,6 +290,18 @@ bool WorldScene::handle_key_event(const KeyEvent& e) { } } + if (e.key == Key::V) { + auto& recording = m_client_world.get_audio().audio_recording(); + if (e.action == KeyAction::PRESS) { + recording.start(); + return true; + } + if (e.action == KeyAction::RELEASE) { + recording.stop(); + return true; + } + } + if (m_paused) { if (m_pasue_menu.handle_event(e)) { return true; diff --git a/src/ui/credits_ui.cpp b/src/ui/credits_ui.cpp index fe4ca2f..de54836 100644 --- a/src/ui/credits_ui.cpp +++ b/src/ui/credits_ui.cpp @@ -60,6 +60,7 @@ void CreditsUI::init() { add_text("protobuf"); add_text("zstd"); add_text("OpenAl Soft"); + add_text("Opus"); add_text("dr_libs"); add_text("nlohmann/json"); add_text("HarfBuzz"); diff --git a/vcpkg.json b/vcpkg.json index 7810023..46685a3 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -5,6 +5,7 @@ "openal-soft", "harfbuzz", "freetype", + "opus", { "name": "sdl3", "default-features": false