add some code
This commit is contained in:
1
managed_components/78__esp-opus-encoder/.component_hash
Normal file
1
managed_components/78__esp-opus-encoder/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
b80e5b6d6dc4bf6b0bf1a3729f52b80500e9e6b1003b3e827b78ba738283a296
|
||||
1
managed_components/78__esp-opus-encoder/.gitignore
vendored
Normal file
1
managed_components/78__esp-opus-encoder/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dist/
|
||||
1
managed_components/78__esp-opus-encoder/CHECKSUMS.json
Normal file
1
managed_components/78__esp-opus-encoder/CHECKSUMS.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-08-09T06:36:59.752264+00:00", "files": [{"path": ".gitignore", "size": 5, "hash": "887f42eeae4276a8ba8ed3e14ec6567107ed2760d18ea7303cc715a38670fbea"}, {"path": "CMakeLists.txt", "size": 183, "hash": "64803247577ebe4b56fb98a7fcf26ab8de7b6c1853e8b684a525df6070e5e5fc"}, {"path": "idf_component.yml", "size": 245, "hash": "6eb7903c5edaf878003897ad251e69c2119d8a69394e9b5b62f6074b3b5b4e09"}, {"path": "opus_decoder.cc", "size": 1470, "hash": "e9a55ca543ac144a8e906a5c91f397335b22ca1fd183cf45d0476cbf52c9c324"}, {"path": "opus_encoder.cc", "size": 3281, "hash": "8e8d92d9040b85a29c79d00071e0d9b6fb955641269b624574d59b722497cfe2"}, {"path": "opus_resampler.cc", "size": 1139, "hash": "9303c8e3fc5bd28ed63302114eaca33b51a3634f43c278b1856223846302b77c"}, {"path": "silk_resampler.h", "size": 1361, "hash": "afac70e0c296c93cf1b24710255f96b99d44df3d8de82ef72a5a4ead59c1ecbe"}, {"path": "include/opus_decoder.h", "size": 729, "hash": "7ea7d09e2aef14b6affd38470ddb1909fd16f6d4f0ee492c29b211374ff3f982"}, {"path": "include/opus_encoder.h", "size": 1046, "hash": "c4858a454c2bca4a2ba89cbd9bf8a04f3de6fa60167a09d5a73844cbaad32d51"}, {"path": "include/opus_resampler.h", "size": 651, "hash": "89b4d2f5e0d7b626b9c40b326072d80941b71a3448ca3f50394f1694c9d64b7d"}, {"path": "include/resampler_structs.h", "size": 2615, "hash": "b038e84c7d79bcd31bbf524dba64103d17ab66c5006cd2b6ad7ba3289226cd18"}]}
|
||||
10
managed_components/78__esp-opus-encoder/CMakeLists.txt
Normal file
10
managed_components/78__esp-opus-encoder/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"opus_encoder.cc"
|
||||
"opus_decoder.cc"
|
||||
"opus_resampler.cc"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"."
|
||||
)
|
||||
11
managed_components/78__esp-opus-encoder/idf_component.yml
Normal file
11
managed_components/78__esp-opus-encoder/idf_component.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
78/esp-opus: ^1.0.5
|
||||
idf: '>=5.3'
|
||||
description: ESP32 Opus Encoder C++ wrapper
|
||||
files:
|
||||
exclude:
|
||||
- .git
|
||||
license: MIT
|
||||
repository: https://github.com/78/esp-opus-encoder
|
||||
url: https://github.com/78/esp-opus-encoder
|
||||
version: 2.4.1
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _OPUS_DECODER_WRAPPER_H_
|
||||
#define _OPUS_DECODER_WRAPPER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include "opus.h"
|
||||
|
||||
|
||||
class OpusDecoderWrapper {
|
||||
public:
|
||||
OpusDecoderWrapper(int sample_rate, int channels, int duration_ms = 60);
|
||||
~OpusDecoderWrapper();
|
||||
|
||||
bool Decode(std::vector<uint8_t>&& opus, std::vector<int16_t>& pcm);
|
||||
void ResetState();
|
||||
|
||||
inline int sample_rate() const {
|
||||
return sample_rate_;
|
||||
}
|
||||
|
||||
inline int duration_ms() const {
|
||||
return duration_ms_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
struct OpusDecoder* audio_dec_ = nullptr;
|
||||
int frame_size_;
|
||||
int sample_rate_;
|
||||
int duration_ms_;
|
||||
};
|
||||
|
||||
#endif // _OPUS_DECODER_WRAPPER_H_
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef _OPUS_ENCODER_WRAPPER_H_
|
||||
#define _OPUS_ENCODER_WRAPPER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include "opus.h"
|
||||
|
||||
#define MAX_OPUS_PACKET_SIZE 1000
|
||||
|
||||
|
||||
class OpusEncoderWrapper {
|
||||
public:
|
||||
OpusEncoderWrapper(int sample_rate, int channels, int duration_ms = 60);
|
||||
~OpusEncoderWrapper();
|
||||
|
||||
inline int sample_rate() const {
|
||||
return sample_rate_;
|
||||
}
|
||||
|
||||
inline int duration_ms() const {
|
||||
return duration_ms_;
|
||||
}
|
||||
|
||||
void SetDtx(bool enable);
|
||||
void SetComplexity(int complexity);
|
||||
bool Encode(std::vector<int16_t>&& pcm, std::vector<uint8_t>& opus);
|
||||
void Encode(std::vector<int16_t>&& pcm, std::function<void(std::vector<uint8_t>&& opus)> handler);
|
||||
bool IsBufferEmpty() const { return in_buffer_.empty(); }
|
||||
void ResetState();
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
struct OpusEncoder* audio_enc_ = nullptr;
|
||||
int sample_rate_;
|
||||
int duration_ms_;
|
||||
int frame_size_;
|
||||
std::vector<int16_t> in_buffer_;
|
||||
};
|
||||
|
||||
#endif // _OPUS_ENCODER_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef OPUS_RESAMPLER_H
|
||||
#define OPUS_RESAMPLER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "opus.h"
|
||||
#include "resampler_structs.h"
|
||||
|
||||
class OpusResampler {
|
||||
public:
|
||||
OpusResampler();
|
||||
~OpusResampler();
|
||||
|
||||
void Configure(int input_sample_rate, int output_sample_rate);
|
||||
void Process(const int16_t *input, int input_samples, int16_t *output);
|
||||
int GetOutputSamples(int input_samples) const;
|
||||
|
||||
int input_sample_rate() const { return input_sample_rate_; }
|
||||
int output_sample_rate() const { return output_sample_rate_; }
|
||||
|
||||
private:
|
||||
silk_resampler_state_struct resampler_state_;
|
||||
int input_sample_rate_;
|
||||
int output_sample_rate_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) 2006-2011, Skype Limited. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SILK_RESAMPLER_STRUCTS_H
|
||||
#define SILK_RESAMPLER_STRUCTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SILK_RESAMPLER_MAX_FIR_ORDER 36
|
||||
#define SILK_RESAMPLER_MAX_IIR_ORDER 6
|
||||
|
||||
typedef struct _silk_resampler_state_struct{
|
||||
opus_int32 sIIR[ SILK_RESAMPLER_MAX_IIR_ORDER ]; /* this must be the first element of this struct */
|
||||
union{
|
||||
opus_int32 i32[ SILK_RESAMPLER_MAX_FIR_ORDER ];
|
||||
opus_int16 i16[ SILK_RESAMPLER_MAX_FIR_ORDER ];
|
||||
} sFIR;
|
||||
opus_int16 delayBuf[ 48 ];
|
||||
opus_int resampler_function;
|
||||
opus_int batchSize;
|
||||
opus_int32 invRatio_Q16;
|
||||
opus_int FIR_Order;
|
||||
opus_int FIR_Fracs;
|
||||
opus_int Fs_in_kHz;
|
||||
opus_int Fs_out_kHz;
|
||||
opus_int inputDelay;
|
||||
const opus_int16 *Coefs;
|
||||
} silk_resampler_state_struct;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SILK_RESAMPLER_STRUCTS_H */
|
||||
|
||||
50
managed_components/78__esp-opus-encoder/opus_decoder.cc
Normal file
50
managed_components/78__esp-opus-encoder/opus_decoder.cc
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "opus_decoder.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "OpusDecoderWrapper"
|
||||
|
||||
OpusDecoderWrapper::OpusDecoderWrapper(int sample_rate, int channels, int duration_ms)
|
||||
: sample_rate_(sample_rate), duration_ms_(duration_ms) {
|
||||
int error;
|
||||
audio_dec_ = opus_decoder_create(sample_rate, channels, &error);
|
||||
if (audio_dec_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create audio decoder, error code: %d", error);
|
||||
return;
|
||||
}
|
||||
|
||||
frame_size_ = sample_rate / 1000 * channels * duration_ms;
|
||||
}
|
||||
|
||||
OpusDecoderWrapper::~OpusDecoderWrapper() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_dec_ != nullptr) {
|
||||
opus_decoder_destroy(audio_dec_);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpusDecoderWrapper::Decode(std::vector<uint8_t>&& opus, std::vector<int16_t>& pcm) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_dec_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Audio decoder is not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
pcm.resize(frame_size_);
|
||||
auto ret = opus_decode(audio_dec_, opus.data(), opus.size(), pcm.data(), pcm.size(), 0);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to decode audio, error code: %d", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resize the pcm vector to the actual decoded samples
|
||||
pcm.resize(ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpusDecoderWrapper::ResetState() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_dec_ != nullptr) {
|
||||
opus_decoder_ctl(audio_dec_, OPUS_RESET_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
102
managed_components/78__esp-opus-encoder/opus_encoder.cc
Normal file
102
managed_components/78__esp-opus-encoder/opus_encoder.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "opus_encoder.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "OpusEncoderWrapper"
|
||||
|
||||
OpusEncoderWrapper::OpusEncoderWrapper(int sample_rate, int channels, int duration_ms)
|
||||
: sample_rate_(sample_rate), duration_ms_(duration_ms) {
|
||||
int error;
|
||||
audio_enc_ = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &error);
|
||||
if (audio_enc_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create audio encoder, error code: %d", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default DTX enabled
|
||||
SetDtx(true);
|
||||
// Complexity 5 almost uses up all CPU of ESP32C3 while complexity 0 uses the least
|
||||
SetComplexity(0);
|
||||
|
||||
frame_size_ = sample_rate / 1000 * channels * duration_ms;
|
||||
}
|
||||
|
||||
OpusEncoderWrapper::~OpusEncoderWrapper() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_destroy(audio_enc_);
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoderWrapper::Encode(std::vector<int16_t>&& pcm, std::function<void(std::vector<uint8_t>&& opus)> handler) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_enc_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Audio encoder is not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_buffer_.empty()) {
|
||||
in_buffer_ = std::move(pcm);
|
||||
} else {
|
||||
/* ISSUE: https://github.com/78/esp-opus-encoder/issues/1 */
|
||||
in_buffer_.reserve(in_buffer_.size() + pcm.size());
|
||||
in_buffer_.insert(in_buffer_.end(), pcm.begin(), pcm.end());
|
||||
}
|
||||
|
||||
while (in_buffer_.size() >= frame_size_) {
|
||||
uint8_t opus[MAX_OPUS_PACKET_SIZE];
|
||||
auto ret = opus_encode(audio_enc_, in_buffer_.data(), frame_size_, opus, MAX_OPUS_PACKET_SIZE);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to encode audio, error code: %ld", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler != nullptr) {
|
||||
handler(std::vector<uint8_t>(opus, opus + ret));
|
||||
}
|
||||
|
||||
in_buffer_.erase(in_buffer_.begin(), in_buffer_.begin() + frame_size_);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpusEncoderWrapper::Encode(std::vector<int16_t>&& pcm, std::vector<uint8_t>& opus) {
|
||||
if (audio_enc_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Audio encoder is not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pcm.size() != frame_size_) {
|
||||
ESP_LOGE(TAG, "Audio data size is not equal to frame size, size: %u, frame size: %u", pcm.size(), frame_size_);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[MAX_OPUS_PACKET_SIZE];
|
||||
auto ret = opus_encode(audio_enc_, pcm.data(), frame_size_, buf, MAX_OPUS_PACKET_SIZE);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to encode audio, error code: %ld", ret);
|
||||
return false;
|
||||
}
|
||||
opus.assign(buf, buf + ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpusEncoderWrapper::ResetState() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_ctl(audio_enc_, OPUS_RESET_STATE);
|
||||
in_buffer_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoderWrapper::SetDtx(bool enable) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_ctl(audio_enc_, OPUS_SET_DTX(enable ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoderWrapper::SetComplexity(int complexity) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_ctl(audio_enc_, OPUS_SET_COMPLEXITY(complexity));
|
||||
}
|
||||
}
|
||||
34
managed_components/78__esp-opus-encoder/opus_resampler.cc
Normal file
34
managed_components/78__esp-opus-encoder/opus_resampler.cc
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "opus_resampler.h"
|
||||
#include "silk_resampler.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "OpusResampler"
|
||||
|
||||
OpusResampler::OpusResampler() {
|
||||
}
|
||||
|
||||
OpusResampler::~OpusResampler() {
|
||||
}
|
||||
|
||||
void OpusResampler::Configure(int input_sample_rate, int output_sample_rate) {
|
||||
int encode = input_sample_rate > output_sample_rate ? 1 : 0;
|
||||
auto ret = silk_resampler_init(&resampler_state_, input_sample_rate, output_sample_rate, encode);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to initialize resampler");
|
||||
return;
|
||||
}
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
ESP_LOGI(TAG, "Resampler configured with input sample rate %d and output sample rate %d", input_sample_rate_, output_sample_rate_);
|
||||
}
|
||||
|
||||
void OpusResampler::Process(const int16_t *input, int input_samples, int16_t *output) {
|
||||
auto ret = silk_resampler(&resampler_state_, output, input, input_samples);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to process resampler");
|
||||
}
|
||||
}
|
||||
|
||||
int OpusResampler::GetOutputSamples(int input_samples) const {
|
||||
return input_samples * output_sample_rate_ / input_sample_rate_;
|
||||
}
|
||||
26
managed_components/78__esp-opus-encoder/silk_resampler.h
Normal file
26
managed_components/78__esp-opus-encoder/silk_resampler.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _SILK_RESAMPLER_H_
|
||||
#define _SILK_RESAMPLER_H_
|
||||
|
||||
#include "opus.h"
|
||||
#include "resampler_structs.h"
|
||||
/*!
|
||||
* Initialize/reset the resampler state for a given pair of input/output sampling rates
|
||||
*/
|
||||
extern "C" opus_int silk_resampler_init(
|
||||
silk_resampler_state_struct *S, /* I/O Resampler state */
|
||||
opus_int32 Fs_Hz_in, /* I Input sampling rate (Hz) */
|
||||
opus_int32 Fs_Hz_out, /* I Output sampling rate (Hz) */
|
||||
opus_int forEnc /* I If 1: encoder; if 0: decoder */
|
||||
);
|
||||
|
||||
/*!
|
||||
* Resampler: convert from one sampling rate to another
|
||||
*/
|
||||
extern "C" opus_int silk_resampler(
|
||||
silk_resampler_state_struct *S, /* I/O Resampler state */
|
||||
opus_int16 out[], /* O Output signal */
|
||||
const opus_int16 in[], /* I Input signal */
|
||||
opus_int32 inLen /* I Number of input samples */
|
||||
);
|
||||
|
||||
#endif // _SILK_RESAMPLER_H_
|
||||
Reference in New Issue
Block a user