diff --git a/README.md b/README.md index 61a44f85..168a4b79 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,28 @@ -# vaudio_lyra +# lyra as a DLL -Speech codec plugin for voice that uses [Lyra](https://github.com/google/lyra), the new voice codec that achieves the best quality-to-bitrate ratio. - -This was written for usage with TFORevive, and so may need adjustments for use with other Source Engine games (missing `bool bFinal` param). +This is originally forked from [Lyra](https://github.com/google/lyra), then after some small changes from the TOFRevive team, I tweaked it to be a simple DLL. +Lyra has amazing quality-to-bitrate ratio and has some features that I don't expose (at present) such as automatically generating pleasant noise when packets are late. ## Compile (on Windows) -You have to have [Bazelisk installed](https://bazel.build/install/bazelisk), and also MSVC and Python (that has `numpy` and `six` installed with pip). Replace `C:\\Python311\\python.exe` below with your Python installation. - -First run the following to convert model files into a header file that will be bundled into the DLL: -``` -C:\\Python311\\python.exe .\models_to_header.py -``` +I find that Python and Bazel are not to my liking, so I built everything in a Windows VM using VirtualBox. The steps are relatively straightforward and reproducible. -Then to compile: +Download and install: https://aka.ms/vs/17/release/vs_BuildTools.exe and select Desktop Development with C++ +Download and install: https://github.com/git-for-windows/git/releases/download/v2.46.0.windows.1/Git-2.46.0-64-bit.exe just take all defaults. +Download and the Windows exe and rename it to bazel.exe https://github.com/bazelbuild/bazel/releases/tag/5.3.2 +Download and install: https://www.python.org/ftp/python/3.12.6/python-3.12.6-amd64.exe -``` -bazel build -c opt --action_env PYTHON_BIN_PATH="C:\\Python311\\python.exe" vaudio_lyra:vaudio_lyra -``` +Open a GIT BASH command window: + git clone https://github.com/google/lyra.git + cd lyra + export PATH=$PATH:/c/Users/User/AppData/Local/Programs/Python/Python312 + python -m pip install setuptools numpy six + python ./models_to_header.py + bazel.exe build -c opt --config=windows --action_env=PYTHON_BIN_PATH="/c/Users/User/AppData/Local/Programs/Python/Python312/python.exe" dll:dll You can replace `-c opt` with `-c dbg` to build in debug mode (with asserts enabled). -Final file is at `./bazel-bin/vaudio_lyra/vaudio_lyra.dll` +Final file is at `./bazel-bin/dll/lyra_dll.dll` ## Compiling CLI examples @@ -40,4 +41,3 @@ Running the built-in unit tests of Lyra might be useful, as we did some changes bazel test --action_env PYTHON_BIN_PATH="C:\\Python311\\python.exe" //lyra:all ``` -We don't currently have any vaudio_lyra-specific unit tests. diff --git a/WORKSPACE b/WORKSPACE index 388661cb..f938a5c3 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -163,6 +163,16 @@ maven_install( ) +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "gflags", + urls = ["https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.tar.gz"], + strip_prefix = "gflags-2.2.2", + sha256 = "34af2f15cf7367513b352bdcd2493ab14ce43692d2dcd9dfc499492966c64dcf", +) + + # Begin Tensorflow WORKSPACE subset required for TFLite git_repository( diff --git a/vaudio_lyra/BUILD b/dll/BUILD similarity index 87% rename from vaudio_lyra/BUILD rename to dll/BUILD index 544d32ae..361e59f9 100644 --- a/vaudio_lyra/BUILD +++ b/dll/BUILD @@ -1,11 +1,7 @@ cc_binary( - name = "vaudio_lyra", + name = "lyra_dll", srcs = [ - "lyravoicecodec.cc", "dllmain.cc", - "frameencoder.cc", - "ivoicecodec.h", - "iframeencoder.h", ], #data = [":tflite_testdata"], linkopts = select({ diff --git a/dll/dllmain.cc b/dll/dllmain.cc new file mode 100644 index 00000000..da9755a8 --- /dev/null +++ b/dll/dllmain.cc @@ -0,0 +1,96 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include "Windows.h" + +#include "lyra/lyra_config.h" +#include "lyra/lyra_encoder.h" +#include "lyra/lyra_decoder.h" +#include "lyra/model_coeffs/_models.h" + +#define BYTES_PER_SAMPLE 2 + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +std::unique_ptr m_Encoder = nullptr; +std::unique_ptr m_Decoder = nullptr; + +extern "C" __declspec(dllexport) bool Initialize() +{ + const int samplerate = 16000; + const int bitrate = 3200; + + const chromemedia::codec::LyraModels models = GetEmbeddedLyraModels(); + + if (!m_Encoder) + { + m_Encoder = chromemedia::codec::LyraEncoder::Create(samplerate, 1, bitrate, false, models); + } + if (!m_Decoder) + { + m_Decoder = chromemedia::codec::LyraDecoder::Create(samplerate, 1, models); + } + return m_Encoder != nullptr && m_Decoder != nullptr; +} + +extern "C" __declspec(dllexport) void Shutdown() +{ + m_Encoder.reset(); + m_Decoder.reset(); +} + +extern "C" __declspec(dllexport) void Encode(const int16_t* uncompressed, size_t uncompressed_size, uint8_t* compressed, size_t compressed_size) +{ + const int num_samples_per_packet = m_Encoder->sample_rate_hz() / m_Encoder->frame_rate(); + const int raw_frame_size = num_samples_per_packet * BYTES_PER_SAMPLE; + + assert(uncompressed_size >= num_samples_per_packet); + + std::vector uncompressed_vector(uncompressed, uncompressed + num_samples_per_packet); + std::optional> encoded = m_Encoder->Encode(uncompressed_vector); + + if (!encoded.has_value()) + { + return; + } + + assert(encoded->size() == chromemedia::codec::BitrateToPacketSize(m_Encoder->bitrate())); + assert(compressed_size >= encoded->size()); + + memcpy_s(compressed, compressed_size, encoded->data(), encoded->size()); +} + +extern "C" __declspec(dllexport) void Decode(const int8_t* compressed, size_t compressed_size, uint16_t* uncompressed, size_t uncompressed_size) +{ + const int num_samples_per_packet = m_Encoder->sample_rate_hz() / m_Encoder->frame_rate(); + const int packet_size = chromemedia::codec::BitrateToPacketSize(m_Encoder->bitrate()); + + assert(compressed_size == packet_size); + + bool valid = m_Decoder->SetEncodedPacket(absl::MakeSpan(reinterpret_cast(compressed), compressed_size)); + assert(valid == true); + if (!valid) return; + + std::optional> decoded = m_Decoder->DecodeSamples(num_samples_per_packet); + if (!decoded.has_value()) + { + assert(decoded.has_value()); + return; + } + + assert(decoded->size() == num_samples_per_packet); + assert(uncompressed_size >= decoded->size()); + + memcpy_s(uncompressed, uncompressed_size * sizeof(uint16_t), decoded->data(), decoded->size() * sizeof(uint16_t)); +} diff --git a/vaudio_lyra/dllmain.cc b/vaudio_lyra/dllmain.cc deleted file mode 100644 index eaa69f06..00000000 --- a/vaudio_lyra/dllmain.cc +++ /dev/null @@ -1,21 +0,0 @@ -// dllmain.cpp : Defines the entry point for the DLL application. -//#include "pch.h" -#define WIN32_LEAN_AND_MEAN -#include "Windows.h" - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - diff --git a/vaudio_lyra/frameencoder.cc b/vaudio_lyra/frameencoder.cc deleted file mode 100644 index 8c00677c..00000000 --- a/vaudio_lyra/frameencoder.cc +++ /dev/null @@ -1,175 +0,0 @@ -#include "ivoicecodec.h" -#include "iframeencoder.h" - -#include -#include - -#ifndef NDEBUG -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include "Windows.h" -#endif - -#ifndef min -#define min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -// VoiceCodec_Frame can be used to wrap a frame encoder for the engine. As it gets sound data, it will queue it -// until it has enough for a frame, then it will compress it. Same thing for decompression. -class VoiceCodec_Frame : public IVoiceCodec -{ -public: - enum { MAX_FRAMEBUFFER_SAMPLES = 1024 }; - - VoiceCodec_Frame(IFrameEncoder* pEncoder) - { - m_nEncodeBufferSamples = 0; - m_nRawBytes = m_nRawSamples = m_nEncodedBytes = 0; - m_pFrameEncoder = pEncoder; - } - - virtual ~VoiceCodec_Frame() - { - if (m_pFrameEncoder) - m_pFrameEncoder->Release(); - } - - virtual bool Init(int quality, unsigned int nSamplesPerSec) - { - if (m_pFrameEncoder && m_pFrameEncoder->Init(quality, nSamplesPerSec, m_nRawBytes, m_nEncodedBytes)) - { - m_nRawSamples = m_nRawBytes / BYTES_PER_SAMPLE; - assert(m_nRawBytes <= MAX_FRAMEBUFFER_SAMPLES && m_nEncodedBytes <= MAX_FRAMEBUFFER_SAMPLES); - return true; - } - else - { - if (m_pFrameEncoder) - m_pFrameEncoder->Release(); - - m_pFrameEncoder = NULL; - return false; - } - } - - virtual void Release() - { - delete this; - } - - virtual int Compress(const char* pUncompressedBytes, int nSamples, char* pCompressed, int maxCompressedBytes/*, bool bFinal*/) - { - if (!m_pFrameEncoder) - return 0; - - const short* pUncompressed = (const short*)pUncompressedBytes; - absl::Span compressed{ reinterpret_cast(pCompressed), static_cast(maxCompressedBytes) }; -#ifndef NDEBUG - memset(pCompressed, 0, maxCompressedBytes); // for asserts to work -#endif - - int nCompressedBytes = 0; - while ((nSamples + m_nEncodeBufferSamples) >= m_nRawSamples && (maxCompressedBytes - nCompressedBytes) >= m_nEncodedBytes) - { - // Get the data block out. - short samples[MAX_FRAMEBUFFER_SAMPLES]; - memcpy_s(samples, MAX_FRAMEBUFFER_SAMPLES, m_EncodeBuffer, m_nEncodeBufferSamples * BYTES_PER_SAMPLE); - memcpy_s(&samples[m_nEncodeBufferSamples], sizeof(samples) - (&samples[m_nEncodeBufferSamples] - samples), - pUncompressed, (m_nRawSamples - m_nEncodeBufferSamples) * BYTES_PER_SAMPLE); - nSamples -= m_nRawSamples - m_nEncodeBufferSamples; - pUncompressed += m_nRawSamples - m_nEncodeBufferSamples; - m_nEncodeBufferSamples = 0; - - // Compress it. - //m_pFrameEncoder->EncodeFrame((const char*)samples, MAX_FRAMEBUFFER_SAMPLES, &pCompressed[nCompressedBytes], maxCompressedBytes); - m_pFrameEncoder->EncodeFrame(absl::MakeConstSpan(samples), compressed.subspan(nCompressedBytes)); - nCompressedBytes += m_nEncodedBytes; - - // Ensure it didn't write more bytes than expected. - assert(compressed.size() == nCompressedBytes || compressed[nCompressedBytes] == 0x00); - } - - // Store the remaining samples. - int nNewSamples = min(nSamples, min(m_nRawSamples - m_nEncodeBufferSamples, m_nRawSamples)); - if (nNewSamples) - { - memcpy_s(&m_EncodeBuffer[m_nEncodeBufferSamples], sizeof(m_EncodeBuffer) - (&m_EncodeBuffer[m_nEncodeBufferSamples] - m_EncodeBuffer), - &pUncompressed[nSamples - nNewSamples], nNewSamples * BYTES_PER_SAMPLE); - m_nEncodeBufferSamples += nNewSamples; - } - -// Respawn's Source Engine does not have the bFinal field -#if 0 - // If it must get the last data, just pad with zeros.. - if (bFinal && m_nEncodeBufferSamples && (maxCompressedBytes - nCompressedBytes) >= m_nEncodedBytes) - { - auto size = (m_nRawSamples - m_nEncodeBufferSamples) * BYTES_PER_SAMPLE; - memset(&m_EncodeBuffer[m_nEncodeBufferSamples], 0, size); - m_pFrameEncoder->EncodeFrame(absl::MakeConstSpan(m_EncodeBuffer, size / sizeof(*m_EncodeBuffer)), compressed.subspan(nCompressedBytes)); - nCompressedBytes += m_nEncodedBytes; - m_nEncodeBufferSamples = 0; - } -#endif - -#ifndef NDEBUG - char dbgbuf[256]; - sprintf_s(dbgbuf, "VoiceCodec_Frame[VoiceEncoder_Lyra]::Compress nCompressedBytes:%i nSamples:%i\n", - nCompressedBytes, nSamples); - OutputDebugStringA(dbgbuf); -#endif - return nCompressedBytes; - } - - virtual int Decompress(const char* pCompressed, int compressedBytes, char* pUncompressed, int maxUncompressedBytes) - { - if (!m_pFrameEncoder) - return 0; - - assert((compressedBytes % m_nEncodedBytes) == 0); - int nDecompressedBytes = 0; - int curCompressedByte = 0; - absl::Span compressed{ reinterpret_cast(pCompressed), static_cast(compressedBytes) }; - absl::Span uncompressed{ reinterpret_cast(pUncompressed), static_cast(maxUncompressedBytes) }; - while ((compressedBytes - curCompressedByte) >= m_nEncodedBytes && (maxUncompressedBytes - nDecompressedBytes) >= m_nRawBytes) - { - m_pFrameEncoder->DecodeFrame(compressed.subspan(curCompressedByte, m_nEncodedBytes), uncompressed.subspan(nDecompressedBytes / BYTES_PER_SAMPLE)); - curCompressedByte += m_nEncodedBytes; - nDecompressedBytes += m_nRawBytes; - } - -#ifndef NDEBUG - char dbgbuf[256]; - sprintf_s(dbgbuf, "VoiceCodec_Frame[VoiceEncoder_Lyra]::Decompress nDecompressedBytes:%i compressedBytes:%i\n", - nDecompressedBytes, compressedBytes); - OutputDebugStringA(dbgbuf); -#endif - return nDecompressedBytes / BYTES_PER_SAMPLE; - } - - virtual bool ResetState() - { - if (m_pFrameEncoder) - return m_pFrameEncoder->ResetState(); - else - return false; - } - - -public: - // The codec encodes and decodes samples in fixed-size blocks, so we queue up uncompressed and decompressed data - // until we have blocks large enough to give to the codec. - short m_EncodeBuffer[MAX_FRAMEBUFFER_SAMPLES]; - int m_nEncodeBufferSamples; - - IFrameEncoder* m_pFrameEncoder; - // Params set on Init by the wrapped encoder: - int m_nRawBytes, m_nRawSamples; - int m_nEncodedBytes; -}; - - -IVoiceCodec* CreateVoiceCodec_Frame(IFrameEncoder* pEncoder) -{ - return new VoiceCodec_Frame(pEncoder); -} diff --git a/vaudio_lyra/iframeencoder.h b/vaudio_lyra/iframeencoder.h deleted file mode 100644 index fb483766..00000000 --- a/vaudio_lyra/iframeencoder.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "absl/types/span.h" - -// A frame encoder is a codec that encodes and decodes data in fixed-size frames. -// VoiceCodec_Frame handles queuing of data and the IVoiceCodec interface. -class IFrameEncoder -{ -public: - virtual ~IFrameEncoder() {} - - // This is called by VoiceCodec_Frame to see if it can initialize.. - // Fills in the uncompressed and encoded frame size (both are in BYTES). - virtual bool Init(int quality, int samplerate, int& rawFrameSize, int& encodedFrameSize) = 0; - - virtual void Release() = 0; - - // pUncompressed is 8-bit signed mono sound data with 'rawFrameSize' bytes. - // pCompressed is the size of encodedFrameSize. - virtual void EncodeFrame(const absl::Span uncompressed, const absl::Span compressed) = 0; - - // pCompressed is encodedFrameSize. - // pDecompressed is where the 8-bit signed mono samples are stored and has space for 'rawFrameSize' bytes. - virtual void DecodeFrame(const absl::Span compressed, const absl::Span uncompressed) = 0; - - // Some codecs maintain state between Compress and Decompress calls. This should clear that state. - virtual bool ResetState() = 0; -}; - -class IVoiceCodec; -extern IVoiceCodec* CreateVoiceCodec_Frame(IFrameEncoder* pEncoder); diff --git a/vaudio_lyra/ivoicecodec.h b/vaudio_lyra/ivoicecodec.h deleted file mode 100644 index ae861a0e..00000000 --- a/vaudio_lyra/ivoicecodec.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef IVOICECODEC_H -#define IVOICECODEC_H -#pragma once - - - -#define BYTES_PER_SAMPLE 2 - - -// This interface is for voice codecs to implement. - -// Codecs are guaranteed to be called with the exact output from Compress into Decompress (ie: -// data won't be stuck together and sent to Decompress). - -// Decompress is not guaranteed to be called in any specific order relative to Compress, but -// Codecs maintain state between calls, so it is best to call Compress with consecutive voice data -// and decompress likewise. If you call it out of order, it will sound wierd. - -// In the same vein, calling Decompress twice with the same data is a bad idea since the state will be -// expecting the next block of data, not the same block. - -class IVoiceCodec -{ -protected: - virtual ~IVoiceCodec() {} - -public: - // Initialize the object. The uncompressed format is always 8-bit signed mono. - virtual bool Init(int quality, unsigned int nSamplesPerSec) = 0; - - // Use this to delete the object. - virtual void Release() = 0; - - - // Compress the voice data. - // pUncompressed - 16-bit signed mono voice data. - // maxCompressedBytes - The length of the pCompressed buffer. Don't exceed this. - // bFinal - Set to true on the last call to Compress (the user stopped talking). - // Some codecs like big block sizes and will hang onto data you give them in Compress calls. - // When you call with bFinal, the codec will give you compressed data no matter what. - // Return the number of bytes you filled into pCompressed. - virtual int Compress(const char* pUncompressed, int nSamples, char* pCompressed, int maxCompressedBytes/*, bool bFinal*/) = 0; - - // Decompress voice data. pUncompressed is 16-bit signed mono. - virtual int Decompress(const char* pCompressed, int compressedBytes, char* pUncompressed, int maxUncompressedBytes) = 0; - - // Some codecs maintain state between Compress and Decompress calls. This should clear that state. - virtual bool ResetState() = 0; -}; - - -#endif // IVOICECODEC_H diff --git a/vaudio_lyra/lyravoicecodec.cc b/vaudio_lyra/lyravoicecodec.cc deleted file mode 100644 index d61c6c8d..00000000 --- a/vaudio_lyra/lyravoicecodec.cc +++ /dev/null @@ -1,172 +0,0 @@ -#include "ivoicecodec.h" -#include "iframeencoder.h" - -#include "include/ghc/filesystem.hpp" -#include "lyra/lyra_config.h" -#include "lyra/lyra_encoder.h" -#include "lyra/lyra_decoder.h" - -#include "lyra/model_coeffs/_models.h" - -//#pragma comment(lib, "lyra_config.lib") -//#pragma comment(lib, "lyra_encoder.lib") -//#pragma comment(lib, "lyra_decoder.lib") - -#pragma comment(lib, "User32.lib") - -class VoiceEncoder_Lyra : public IFrameEncoder -{ -public: - VoiceEncoder_Lyra() {}; - virtual ~VoiceEncoder_Lyra() {}; - - // Interfaces IFrameDecoder - - bool Init(int quality, int samplerate, int& rawFrameSize, int& encodedFrameSize); - void Release() { delete this; }; - void EncodeFrame(const absl::Span uncompressed, const absl::Span compressed); - void DecodeFrame(const absl::Span compressed, const absl::Span uncompressed); - bool ResetState() { return true; }; - -private: - std::unique_ptr m_Encoder; - std::unique_ptr m_Decoder; -}; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -bool VoiceEncoder_Lyra::Init(int quality, int samplerate, int& rawFrameSize, int& encodedFrameSize) -{ - assert(quality == 3); - assert(samplerate == 16000); - if (!chromemedia::codec::IsSampleRateSupported(samplerate)) - { - char buf[512]; - sprintf_s(buf, "Lyra does not support the requested sample rate: %lu.", samplerate); - MessageBoxA(0, buf, "Lyra init error", MB_ICONERROR); - return false; - } - - // The only supported bitrate values are 3200, 6000, 9200 - //const int bitrate = 3200; // 3.2 Kb/s = 3200 b/s = 400 B/s - //const int bitrate = 6000; // 6 Kb/s = 6000 b/s = 750 B/s - const int bitrate = 9200; // 9.2 Kb/s = 9200 b/s = 1150 B/s - - const chromemedia::codec::LyraModels models = GetEmbeddedLyraModels(); - - m_Encoder = chromemedia::codec::LyraEncoder::Create(/*sample_rate_hz=*/samplerate, // <=kInternalSampleRateHz (using Lyra's native sample rate to avoid using resampler...) - /*num_channels=*/1, - /*bitrate=*/bitrate, - /*enable_dtx=*/false, - /*models=*/models); - - if (m_Encoder == nullptr) - { - MessageBoxA(0, "Could not create Lyra encoder.", "Lyra init error", MB_ICONERROR); - return false; - } - - m_Decoder = chromemedia::codec::LyraDecoder::Create(samplerate, /*num_channels=*/1, models); - - if (m_Decoder == nullptr) - { - MessageBoxA(0, "Could not create Lyra decoder.", "Lyra init error", MB_ICONERROR); - return false; - } - - const int num_samples_per_packet = m_Encoder->sample_rate_hz() / m_Encoder->frame_rate(); - - rawFrameSize = num_samples_per_packet * BYTES_PER_SAMPLE; - encodedFrameSize = chromemedia::codec::BitrateToPacketSize(m_Encoder->bitrate()); - assert(rawFrameSize == 2 * 16000 / 50); - if (bitrate == 9200) assert(encodedFrameSize == 23); - - //MessageBoxA(0, ("OK, quality:" + std::to_string(quality) + ", samplerate:" + std::to_string(samplerate)).c_str(), "Lyra init", 0); - return true; -} - -void VoiceEncoder_Lyra::EncodeFrame(const absl::Span uncompressed, const absl::Span compressed) -{ - const int num_samples_per_packet = m_Encoder->sample_rate_hz() / m_Encoder->frame_rate(); - const int raw_frame_size = num_samples_per_packet * BYTES_PER_SAMPLE; - - // uncompressed buffer is set to arbitrary big size, it does not equal num_samples_per_packet, - // but it only stores `num_samples_per_packet` amount of samples, and that's how must we must read - assert(uncompressed.size() >= num_samples_per_packet); - auto encoded = m_Encoder->Encode(uncompressed.first(num_samples_per_packet)); - - if (!encoded.has_value()) - { - MessageBoxA(0, "Could not encode Lyra frame.", "Lyra error", MB_ICONERROR); - return; - } - - assert(encoded->size() == chromemedia::codec::BitrateToPacketSize(m_Encoder->bitrate())); - - memcpy_s(compressed.data(), compressed.size(), encoded->data(), encoded->size()); -} - -void VoiceEncoder_Lyra::DecodeFrame(const absl::Span compressed, const absl::Span uncompressed) -{ - const int num_samples_per_packet = m_Encoder->sample_rate_hz() / m_Encoder->frame_rate(); - const int packet_size = chromemedia::codec::BitrateToPacketSize(m_Encoder->bitrate()); - assert(compressed.size() == packet_size); - - bool valid = m_Decoder->SetEncodedPacket(compressed); - if (!valid) - { - //MessageBoxA(0, "Invalid Lyra frame.", "Lyra error", MB_ICONERROR); - assert(valid == true); - return; - } - - auto decoded = m_Decoder->DecodeSamples(num_samples_per_packet); - - if (!decoded.has_value()) - { - //MessageBoxA(0, "Could not decode Lyra frame.", "Lyra error", MB_ICONERROR); - assert(decoded.has_value()); - return; - } - - assert(decoded->size() == num_samples_per_packet); - - memcpy_s(uncompressed.data(), uncompressed.size() * BYTES_PER_SAMPLE, decoded->data(), decoded->size() * BYTES_PER_SAMPLE); -} - -class VoiceCodec_Uncompressed : public IVoiceCodec -{ -public: - VoiceCodec_Uncompressed() {} - virtual ~VoiceCodec_Uncompressed() {} - virtual bool Init(int quality, unsigned int nSamplesPerSec) { return true; } - virtual void Release() { delete this; } - virtual bool ResetState() { return true; } - virtual int Compress(const char* pUncompressed, int nSamples, char* pCompressed, int maxCompressedBytes/*, bool bFinal*/) - { - int nCompressedBytes = nSamples * BYTES_PER_SAMPLE; - memcpy_s(pCompressed, maxCompressedBytes, pUncompressed, nCompressedBytes); - return nCompressedBytes; - } - virtual int Decompress(const char* pCompressed, int compressedBytes, char* pUncompressed, int maxUncompressedBytes) - { - int nDecompressedBytes = compressedBytes; - memcpy_s(pUncompressed, maxUncompressedBytes, pCompressed, compressedBytes); - return nDecompressedBytes / BYTES_PER_SAMPLE; - } -}; - -#define EXPORT __declspec(dllexport) -#define CALLTYPE STDAPICALLTYPE - -extern "C" EXPORT void* CALLTYPE CreateInterface(const char* pName, int* pReturnCode) -{ - assert(strcmp(pName, "vaudio_lyra") == 0); - - //return new VoiceCodec_Uncompressed; - - IFrameEncoder* pEncoder = new VoiceEncoder_Lyra; - return CreateVoiceCodec_Frame(pEncoder); -}