强化代码
This commit is contained in:
parent
3c9ec050c4
commit
a894f22ae5
@ -0,0 +1,29 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.18.1)
|
||||||
|
project(app CXX)
|
||||||
|
|
||||||
|
# ========== 关键修正1:源码路径(CMAKE_CURRENT_SOURCE_DIR 就是 src/main/cpp,无需重复加路径) ==========
|
||||||
|
add_library(
|
||||||
|
app
|
||||||
|
SHARED
|
||||||
|
# 直接写文件名即可,否则会找 src/main/cpp/src/main/cpp/xxx.cpp(路径重复)
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/opus_recorder.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/opus_decoder.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# ========== 关键修正2:正确导入本地 jniLibs 下的 libopus.so ==========
|
||||||
|
# 1. 声明 opus 为「导入库」(SHARED 对应 .so 动态库)
|
||||||
|
add_library(opus SHARED IMPORTED)
|
||||||
|
# 2. 指定 libopus.so 的绝对路径(适配不同架构:arm64-v8a/armeabi-v7a 等)
|
||||||
|
set_target_properties(opus PROPERTIES
|
||||||
|
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopus.so
|
||||||
|
# 可选:如果需要头文件,添加这行(头文件放 src/main/cpp/include/opus/ 下)
|
||||||
|
# INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# ========== 关键修正3:链接导入的 opus 库(直接写库名,不用 opus:: 前缀) ==========
|
||||||
|
target_link_libraries(
|
||||||
|
app
|
||||||
|
PRIVATE
|
||||||
|
opus # 链接上面声明的本地 libopus.so
|
||||||
|
log # Android 日志库
|
||||||
|
)
|
||||||
@ -19,7 +19,8 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
cppFlags ""
|
arguments += "-DANDROID_STL=c++_shared"
|
||||||
|
cppFlags += "-std=c++17"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
buildConfig = true // 显式启用
|
buildConfig = true // 显式启用
|
||||||
|
prefab = true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@ -182,4 +184,5 @@ dependencies {
|
|||||||
|
|
||||||
implementation files('libs/sherpa-onnx-1.12.23.aar')
|
implementation files('libs/sherpa-onnx-1.12.23.aar')
|
||||||
|
|
||||||
|
implementation(libs.opus.v131)
|
||||||
}
|
}
|
||||||
@ -1,3 +1,60 @@
|
|||||||
x iǎo z ì t óng x ué @小智同学
|
x iǎo zh ì t óng x ué
|
||||||
x iǎo zh ì t óng x ué @小智同学
|
x iǎo z ì t óng x ué
|
||||||
x iǎo zh ì t óng x ié @小智同学
|
x iǎo j ì t óng x ué
|
||||||
|
x iǎo ch ì t óng x ué
|
||||||
|
x iáo zh ì t óng x ué
|
||||||
|
x iào zh ì t óng x ué
|
||||||
|
x iǎo zh í t óng x ué
|
||||||
|
x iǎo zh ǐ t óng x ué
|
||||||
|
x iǎo zh ì t ōng x ué
|
||||||
|
x iǎo zh ì t òng x ué
|
||||||
|
x iǎo zh ì t óng x uē
|
||||||
|
x iǎo zh ì t óng x uè
|
||||||
|
x iǎo zh ì t óng y ué
|
||||||
|
x iǎo zh i t óng x ué
|
||||||
|
x iǎo zh ì t óng x uè
|
||||||
|
x iǎo zh ì t óng x ué
|
||||||
|
x iǎo z ì t óng x ué
|
||||||
|
x iǎo j ì t óng x ué
|
||||||
|
x iǎo ch ì t óng x ué
|
||||||
|
x iáo zh ì t óng x ué
|
||||||
|
x iǎo zh í t óng x ué
|
||||||
|
x iǎo zh ì t ōng x ué
|
||||||
|
x iǎo zh ì t óng x uè
|
||||||
|
|
||||||
|
|
||||||
|
x iǎo zh ì d óng x ué
|
||||||
|
x iǎo z ì d óng x ué
|
||||||
|
x iǎo j ì d óng x ué
|
||||||
|
x iǎo ch ì d óng x ué
|
||||||
|
x iáo zh ì d óng x ué
|
||||||
|
x iào zh ì d óng x ué
|
||||||
|
x iǎo zh í d óng x ué
|
||||||
|
x iǎo zh ǐ d óng x ué
|
||||||
|
x iǎo zh ì d ōng x ué
|
||||||
|
x iǎo zh ì d òng x ué
|
||||||
|
x iǎo zh ì d óng x uē
|
||||||
|
x iǎo zh ì d óng x uè
|
||||||
|
x iǎo zh ì d óng y ué
|
||||||
|
x iǎo zh i d óng x ué
|
||||||
|
x iǎo zh ì d óng x uè
|
||||||
|
x iǎo zh ì d óng x ué
|
||||||
|
x iǎo z ì d óng x ué
|
||||||
|
x iǎo j ì d óng x ué
|
||||||
|
x iǎo ch ì d óng x ué
|
||||||
|
x iáo zh ì d óng x ué
|
||||||
|
x iǎo zh í d óng x ué
|
||||||
|
x iǎo zh ì d ōng x ué
|
||||||
|
x iǎo zh ì d óng x uè
|
||||||
|
|
||||||
|
s iǎo zh ì t óng x ué
|
||||||
|
sh iǎo zh ì t óng x ué
|
||||||
|
h iǎo zh ì t óng x ué
|
||||||
|
s iǎo zh ì t óng x ué
|
||||||
|
sh iǎo zh ì t óng x ué
|
||||||
|
|
||||||
|
s iǎo zh ì d óng x ué
|
||||||
|
sh iǎo zh ì d óng x ué
|
||||||
|
h iǎo zh ì d óng x ué
|
||||||
|
x iào zh ì t óng x ué
|
||||||
|
x iào zh ì d óng x ué
|
||||||
|
|||||||
981
app/src/main/cpp/opus.h
Normal file
981
app/src/main/cpp/opus.h
Normal file
@ -0,0 +1,981 @@
|
|||||||
|
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
||||||
|
Written by Jean-Marc Valin and Koen Vos */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file opus.h
|
||||||
|
* @brief Opus reference implementation API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPUS_H
|
||||||
|
#define OPUS_H
|
||||||
|
|
||||||
|
#include "opus_types.h"
|
||||||
|
#include "opus_defines.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mainpage Opus
|
||||||
|
*
|
||||||
|
* The Opus codec is designed for interactive speech and audio transmission over the Internet.
|
||||||
|
* It is designed by the IETF Codec Working Group and incorporates technology from
|
||||||
|
* Skype's SILK codec and Xiph.Org's CELT codec.
|
||||||
|
*
|
||||||
|
* The Opus codec is designed to handle a wide range of interactive audio applications,
|
||||||
|
* including Voice over IP, videoconferencing, in-game chat, and even remote live music
|
||||||
|
* performances. It can scale from low bit-rate narrowband speech to very high quality
|
||||||
|
* stereo music. Its main features are:
|
||||||
|
|
||||||
|
* @li Sampling rates from 8 to 48 kHz
|
||||||
|
* @li Bit-rates from 6 kb/s to 510 kb/s
|
||||||
|
* @li Support for both constant bit-rate (CBR) and variable bit-rate (VBR)
|
||||||
|
* @li Audio bandwidth from narrowband to full-band
|
||||||
|
* @li Support for speech and music
|
||||||
|
* @li Support for mono and stereo
|
||||||
|
* @li Support for multichannel (up to 255 channels)
|
||||||
|
* @li Frame sizes from 2.5 ms to 60 ms
|
||||||
|
* @li Good loss robustness and packet loss concealment (PLC)
|
||||||
|
* @li Floating point and fixed-point implementation
|
||||||
|
*
|
||||||
|
* Documentation sections:
|
||||||
|
* @li @ref opus_encoder
|
||||||
|
* @li @ref opus_decoder
|
||||||
|
* @li @ref opus_repacketizer
|
||||||
|
* @li @ref opus_multistream
|
||||||
|
* @li @ref opus_libinfo
|
||||||
|
* @li @ref opus_custom
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup opus_encoder Opus Encoder
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief This page describes the process and functions used to encode Opus.
|
||||||
|
*
|
||||||
|
* Since Opus is a stateful codec, the encoding process starts with creating an encoder
|
||||||
|
* state. This can be done with:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* int error;
|
||||||
|
* OpusEncoder *enc;
|
||||||
|
* enc = opus_encoder_create(Fs, channels, application, &error);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* From this point, @c enc can be used for encoding an audio stream. An encoder state
|
||||||
|
* @b must @b not be used for more than one stream at the same time. Similarly, the encoder
|
||||||
|
* state @b must @b not be re-initialized for each frame.
|
||||||
|
*
|
||||||
|
* While opus_encoder_create() allocates memory for the state, it's also possible
|
||||||
|
* to initialize pre-allocated memory:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* int size;
|
||||||
|
* int error;
|
||||||
|
* OpusEncoder *enc;
|
||||||
|
* size = opus_encoder_get_size(channels);
|
||||||
|
* enc = malloc(size);
|
||||||
|
* error = opus_encoder_init(enc, Fs, channels, application);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* where opus_encoder_get_size() returns the required size for the encoder state. Note that
|
||||||
|
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||||
|
*
|
||||||
|
* The encoder state is always continuous in memory and only a shallow copy is sufficient
|
||||||
|
* to copy it (e.g. memcpy())
|
||||||
|
*
|
||||||
|
* It is possible to change some of the encoder's settings using the opus_encoder_ctl()
|
||||||
|
* interface. All these settings already default to the recommended value, so they should
|
||||||
|
* only be changed when necessary. The most common settings one may want to change are:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
|
||||||
|
* opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
|
||||||
|
* opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* where
|
||||||
|
*
|
||||||
|
* @arg bitrate is in bits per second (b/s)
|
||||||
|
* @arg complexity is a value from 1 to 10, where 1 is the lowest complexity and 10 is the highest
|
||||||
|
* @arg signal_type is either OPUS_AUTO (default), OPUS_SIGNAL_VOICE, or OPUS_SIGNAL_MUSIC
|
||||||
|
*
|
||||||
|
* See @ref opus_encoderctls and @ref opus_genericctls for a complete list of parameters that can be set or queried. Most parameters can be set or changed at any time during a stream.
|
||||||
|
*
|
||||||
|
* To encode a frame, opus_encode() or opus_encode_float() must be called with exactly one frame (2.5, 5, 10, 20, 40 or 60 ms) of audio data:
|
||||||
|
* @code
|
||||||
|
* len = opus_encode(enc, audio_frame, frame_size, packet, max_packet);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* where
|
||||||
|
* <ul>
|
||||||
|
* <li>audio_frame is the audio data in opus_int16 (or float for opus_encode_float())</li>
|
||||||
|
* <li>frame_size is the duration of the frame in samples (per channel)</li>
|
||||||
|
* <li>packet is the byte array to which the compressed data is written</li>
|
||||||
|
* <li>max_packet is the maximum number of bytes that can be written in the packet (4000 bytes is recommended).
|
||||||
|
* Do not use max_packet to control VBR target bitrate, instead use the #OPUS_SET_BITRATE CTL.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* opus_encode() and opus_encode_float() return the number of bytes actually written to the packet.
|
||||||
|
* The return value <b>can be negative</b>, which indicates that an error has occurred. If the return value
|
||||||
|
* is 2 bytes or less, then the packet does not need to be transmitted (DTX).
|
||||||
|
*
|
||||||
|
* Once the encoder state if no longer needed, it can be destroyed with
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* opus_encoder_destroy(enc);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* If the encoder was created with opus_encoder_init() rather than opus_encoder_create(),
|
||||||
|
* then no action is required aside from potentially freeing the memory that was manually
|
||||||
|
* allocated for it (calling free(enc) for the example above)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Opus encoder state.
|
||||||
|
* This contains the complete state of an Opus encoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_encoder_create,opus_encoder_init
|
||||||
|
*/
|
||||||
|
typedef struct OpusEncoder OpusEncoder;
|
||||||
|
|
||||||
|
/** Gets the size of an <code>OpusEncoder</code> structure.
|
||||||
|
* @param[in] channels <tt>int</tt>: Number of channels.
|
||||||
|
* This must be 1 or 2.
|
||||||
|
* @returns The size in bytes.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_encoder_get_size(int channels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Allocates and initializes an encoder state.
|
||||||
|
* There are three coding modes:
|
||||||
|
*
|
||||||
|
* @ref OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice
|
||||||
|
* signals. It enhances the input signal by high-pass filtering and
|
||||||
|
* emphasizing formants and harmonics. Optionally it includes in-band
|
||||||
|
* forward error correction to protect against packet loss. Use this
|
||||||
|
* mode for typical VoIP applications. Because of the enhancement,
|
||||||
|
* even at high bitrates the output may sound different from the input.
|
||||||
|
*
|
||||||
|
* @ref OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most
|
||||||
|
* non-voice signals like music. Use this mode for music and mixed
|
||||||
|
* (music/voice) content, broadcast, and applications requiring less
|
||||||
|
* than 15 ms of coding delay.
|
||||||
|
*
|
||||||
|
* @ref OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that
|
||||||
|
* disables the speech-optimized mode in exchange for slightly reduced delay.
|
||||||
|
* This mode can only be set on an newly initialized or freshly reset encoder
|
||||||
|
* because it changes the codec delay.
|
||||||
|
*
|
||||||
|
* This is useful when the caller knows that the speech-optimized modes will not be needed (use with caution).
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||||
|
* @param [in] application <tt>int</tt>: Coding mode (@ref OPUS_APPLICATION_VOIP/@ref OPUS_APPLICATION_AUDIO/@ref OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||||
|
* @param [out] error <tt>int*</tt>: @ref opus_errorcodes
|
||||||
|
* @note Regardless of the sampling rate and number channels selected, the Opus encoder
|
||||||
|
* can switch to a lower audio bandwidth or number of channels if the bitrate
|
||||||
|
* selected is too low. This also means that it is safe to always use 48 kHz stereo input
|
||||||
|
* and let the encoder optimize the encoding.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusEncoder *opus_encoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int application,
|
||||||
|
int *error
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Initializes a previously allocated encoder state
|
||||||
|
* The memory pointed to by st must be at least the size returned by opus_encoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of malloc.
|
||||||
|
* @see opus_encoder_create(),opus_encoder_get_size()
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
||||||
|
* @param [in] application <tt>int</tt>: Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||||
|
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_encoder_init(
|
||||||
|
OpusEncoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int application
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Encodes an Opus frame.
|
||||||
|
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] pcm <tt>opus_int16*</tt>: Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(opus_int16)
|
||||||
|
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
||||||
|
* input signal.
|
||||||
|
* This must be an Opus frame size for
|
||||||
|
* the encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted
|
||||||
|
* values are 120, 240, 480, 960, 1920,
|
||||||
|
* and 2880.
|
||||||
|
* Passing in a duration of less than
|
||||||
|
* 10 ms (480 samples at 48 kHz) will
|
||||||
|
* prevent the encoder from using the LPC
|
||||||
|
* or hybrid modes.
|
||||||
|
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode(
|
||||||
|
OpusEncoder *st,
|
||||||
|
const opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Encodes an Opus frame from floating point input.
|
||||||
|
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] pcm <tt>float*</tt>: Input in float format (interleaved if 2 channels), with a normal range of +/-1.0.
|
||||||
|
* Samples with a range beyond +/-1.0 are supported but will
|
||||||
|
* be clipped by decoders using the integer API and should
|
||||||
|
* only be used if it is known that the far end supports
|
||||||
|
* extended dynamic range.
|
||||||
|
* length is frame_size*channels*sizeof(float)
|
||||||
|
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
||||||
|
* input signal.
|
||||||
|
* This must be an Opus frame size for
|
||||||
|
* the encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted
|
||||||
|
* values are 120, 240, 480, 960, 1920,
|
||||||
|
* and 2880.
|
||||||
|
* Passing in a duration of less than
|
||||||
|
* 10 ms (480 samples at 48 kHz) will
|
||||||
|
* prevent the encoder from using the LPC
|
||||||
|
* or hybrid modes.
|
||||||
|
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode_float(
|
||||||
|
OpusEncoder *st,
|
||||||
|
const float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Frees an <code>OpusEncoder</code> allocated by opus_encoder_create().
|
||||||
|
* @param[in] st <tt>OpusEncoder*</tt>: State to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_encoder_destroy(OpusEncoder *st);
|
||||||
|
|
||||||
|
/** Perform a CTL function on an Opus encoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated
|
||||||
|
* by a convenience macro.
|
||||||
|
* @param st <tt>OpusEncoder*</tt>: Encoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls or
|
||||||
|
* @ref opus_encoderctls.
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_encoderctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_decoder Opus Decoder
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @brief This page describes the process and functions used to decode Opus.
|
||||||
|
*
|
||||||
|
* The decoding process also starts with creating a decoder
|
||||||
|
* state. This can be done with:
|
||||||
|
* @code
|
||||||
|
* int error;
|
||||||
|
* OpusDecoder *dec;
|
||||||
|
* dec = opus_decoder_create(Fs, channels, &error);
|
||||||
|
* @endcode
|
||||||
|
* where
|
||||||
|
* @li Fs is the sampling rate and must be 8000, 12000, 16000, 24000, or 48000
|
||||||
|
* @li channels is the number of channels (1 or 2)
|
||||||
|
* @li error will hold the error code in case of failure (or #OPUS_OK on success)
|
||||||
|
* @li the return value is a newly created decoder state to be used for decoding
|
||||||
|
*
|
||||||
|
* While opus_decoder_create() allocates memory for the state, it's also possible
|
||||||
|
* to initialize pre-allocated memory:
|
||||||
|
* @code
|
||||||
|
* int size;
|
||||||
|
* int error;
|
||||||
|
* OpusDecoder *dec;
|
||||||
|
* size = opus_decoder_get_size(channels);
|
||||||
|
* dec = malloc(size);
|
||||||
|
* error = opus_decoder_init(dec, Fs, channels);
|
||||||
|
* @endcode
|
||||||
|
* where opus_decoder_get_size() returns the required size for the decoder state. Note that
|
||||||
|
* future versions of this code may change the size, so no assuptions should be made about it.
|
||||||
|
*
|
||||||
|
* The decoder state is always continuous in memory and only a shallow copy is sufficient
|
||||||
|
* to copy it (e.g. memcpy())
|
||||||
|
*
|
||||||
|
* To decode a frame, opus_decode() or opus_decode_float() must be called with a packet of compressed audio data:
|
||||||
|
* @code
|
||||||
|
* frame_size = opus_decode(dec, packet, len, decoded, max_size, 0);
|
||||||
|
* @endcode
|
||||||
|
* where
|
||||||
|
*
|
||||||
|
* @li packet is the byte array containing the compressed data
|
||||||
|
* @li len is the exact number of bytes contained in the packet
|
||||||
|
* @li decoded is the decoded audio data in opus_int16 (or float for opus_decode_float())
|
||||||
|
* @li max_size is the max duration of the frame in samples (per channel) that can fit into the decoded_frame array
|
||||||
|
*
|
||||||
|
* opus_decode() and opus_decode_float() return the number of samples (per channel) decoded from the packet.
|
||||||
|
* If that value is negative, then an error has occurred. This can occur if the packet is corrupted or if the audio
|
||||||
|
* buffer is too small to hold the decoded audio.
|
||||||
|
*
|
||||||
|
* Opus is a stateful codec with overlapping blocks and as a result Opus
|
||||||
|
* packets are not coded independently of each other. Packets must be
|
||||||
|
* passed into the decoder serially and in the correct order for a correct
|
||||||
|
* decode. Lost packets can be replaced with loss concealment by calling
|
||||||
|
* the decoder with a null pointer and zero length for the missing packet.
|
||||||
|
*
|
||||||
|
* A single codec state may only be accessed from a single thread at
|
||||||
|
* a time and any required locking must be performed by the caller. Separate
|
||||||
|
* streams must be decoded with separate decoder states and can be decoded
|
||||||
|
* in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK
|
||||||
|
* defined.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Opus decoder state.
|
||||||
|
* This contains the complete state of an Opus decoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_decoder_create,opus_decoder_init
|
||||||
|
*/
|
||||||
|
typedef struct OpusDecoder OpusDecoder;
|
||||||
|
|
||||||
|
/** Gets the size of an <code>OpusDecoder</code> structure.
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels.
|
||||||
|
* This must be 1 or 2.
|
||||||
|
* @returns The size in bytes.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_size(int channels);
|
||||||
|
|
||||||
|
/** Allocates and initializes a decoder state.
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sample rate to decode at (Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
||||||
|
* @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
|
||||||
|
*
|
||||||
|
* Internally Opus stores data at 48000 Hz, so that should be the default
|
||||||
|
* value for Fs. However, the decoder can efficiently decode to buffers
|
||||||
|
* at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use
|
||||||
|
* data at the full sample rate, or knows the compressed data doesn't
|
||||||
|
* use the full frequency range, it can request decoding at a reduced
|
||||||
|
* rate. Likewise, the decoder is capable of filling in either mono or
|
||||||
|
* interleaved stereo pcm buffers, at the caller's request.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusDecoder *opus_decoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int *error
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Initializes a previously allocated decoder state.
|
||||||
|
* The state must be at least the size returned by opus_decoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state.
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate to decode to (Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
||||||
|
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_decoder_init(
|
||||||
|
OpusDecoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Decode an Opus packet.
|
||||||
|
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload*
|
||||||
|
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
||||||
|
* is frame_size*channels*sizeof(opus_int16)
|
||||||
|
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
||||||
|
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
||||||
|
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
||||||
|
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
||||||
|
* decoded. If no such data is available, the frame is decoded as if it were lost.
|
||||||
|
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode(
|
||||||
|
OpusDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Decode an Opus packet with floating point output.
|
||||||
|
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload
|
||||||
|
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
||||||
|
* is frame_size*channels*sizeof(float)
|
||||||
|
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
||||||
|
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
||||||
|
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
||||||
|
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
||||||
|
* decoded. If no such data is available the frame is decoded as if it were lost.
|
||||||
|
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode_float(
|
||||||
|
OpusDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Perform a CTL function on an Opus decoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated
|
||||||
|
* by a convenience macro.
|
||||||
|
* @param st <tt>OpusDecoder*</tt>: Decoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls or
|
||||||
|
* @ref opus_decoderctls.
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_decoderctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_decoder_ctl(OpusDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Frees an <code>OpusDecoder</code> allocated by opus_decoder_create().
|
||||||
|
* @param[in] st <tt>OpusDecoder*</tt>: State to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
|
||||||
|
|
||||||
|
/** Parse an opus packet into one or more frames.
|
||||||
|
* Opus_decode will perform this operation internally so most applications do
|
||||||
|
* not need to use this function.
|
||||||
|
* This function does not copy the frames, the returned pointers are pointers into
|
||||||
|
* the input packet.
|
||||||
|
* @param [in] data <tt>char*</tt>: Opus packet to be parsed
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: size of data
|
||||||
|
* @param [out] out_toc <tt>char*</tt>: TOC pointer
|
||||||
|
* @param [out] frames <tt>char*[48]</tt> encapsulated frames
|
||||||
|
* @param [out] size <tt>opus_int16[48]</tt> sizes of the encapsulated frames
|
||||||
|
* @param [out] payload_offset <tt>int*</tt>: returns the position of the payload within the packet (in bytes)
|
||||||
|
* @returns number of frames
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_packet_parse(
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
unsigned char *out_toc,
|
||||||
|
const unsigned char *frames[48],
|
||||||
|
opus_int16 size[48],
|
||||||
|
int *payload_offset
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
/** Gets the bandwidth of an Opus packet.
|
||||||
|
* @param [in] data <tt>char*</tt>: Opus packet
|
||||||
|
* @retval OPUS_BANDWIDTH_NARROWBAND Narrowband (4kHz bandpass)
|
||||||
|
* @retval OPUS_BANDWIDTH_MEDIUMBAND Mediumband (6kHz bandpass)
|
||||||
|
* @retval OPUS_BANDWIDTH_WIDEBAND Wideband (8kHz bandpass)
|
||||||
|
* @retval OPUS_BANDWIDTH_SUPERWIDEBAND Superwideband (12kHz bandpass)
|
||||||
|
* @retval OPUS_BANDWIDTH_FULLBAND Fullband (20kHz bandpass)
|
||||||
|
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Gets the number of samples per frame from an Opus packet.
|
||||||
|
* @param [in] data <tt>char*</tt>: Opus packet.
|
||||||
|
* This must contain at least one byte of
|
||||||
|
* data.
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
||||||
|
* This must be a multiple of 400, or
|
||||||
|
* inaccurate results will be returned.
|
||||||
|
* @returns Number of samples per frame.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Gets the number of channels from an Opus packet.
|
||||||
|
* @param [in] data <tt>char*</tt>: Opus packet
|
||||||
|
* @returns Number of channels
|
||||||
|
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Gets the number of frames in an Opus packet.
|
||||||
|
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||||
|
* @returns Number of frames
|
||||||
|
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||||
|
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Gets the number of samples of an Opus packet.
|
||||||
|
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||||
|
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
||||||
|
* This must be a multiple of 400, or
|
||||||
|
* inaccurate results will be returned.
|
||||||
|
* @returns Number of samples
|
||||||
|
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||||
|
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Gets the number of samples of an Opus packet.
|
||||||
|
* @param [in] dec <tt>OpusDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] packet <tt>char*</tt>: Opus packet
|
||||||
|
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
||||||
|
* @returns Number of samples
|
||||||
|
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
||||||
|
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||||
|
|
||||||
|
/** Applies soft-clipping to bring a float signal within the [-1,1] range. If
|
||||||
|
* the signal is already in that range, nothing is done. If there are values
|
||||||
|
* outside of [-1,1], then the signal is clipped as smoothly as possible to
|
||||||
|
* both fit in the range and avoid creating excessive distortion in the
|
||||||
|
* process.
|
||||||
|
* @param [in,out] pcm <tt>float*</tt>: Input PCM and modified PCM
|
||||||
|
* @param [in] frame_size <tt>int</tt> Number of samples per channel to process
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @param [in,out] softclip_mem <tt>float*</tt>: State memory for the soft clipping process (one float per channel, initialized to zero)
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem);
|
||||||
|
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_repacketizer Repacketizer
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* The repacketizer can be used to merge multiple Opus packets into a single
|
||||||
|
* packet or alternatively to split Opus packets that have previously been
|
||||||
|
* merged. Splitting valid Opus packets is always guaranteed to succeed,
|
||||||
|
* whereas merging valid packets only succeeds if all frames have the same
|
||||||
|
* mode, bandwidth, and frame size, and when the total duration of the merged
|
||||||
|
* packet is no more than 120 ms. The 120 ms limit comes from the
|
||||||
|
* specification and limits decoder memory requirements at a point where
|
||||||
|
* framing overhead becomes negligible.
|
||||||
|
*
|
||||||
|
* The repacketizer currently only operates on elementary Opus
|
||||||
|
* streams. It will not manipualte multistream packets successfully, except in
|
||||||
|
* the degenerate case where they consist of data from a single stream.
|
||||||
|
*
|
||||||
|
* The repacketizing process starts with creating a repacketizer state, either
|
||||||
|
* by calling opus_repacketizer_create() or by allocating the memory yourself,
|
||||||
|
* e.g.,
|
||||||
|
* @code
|
||||||
|
* OpusRepacketizer *rp;
|
||||||
|
* rp = (OpusRepacketizer*)malloc(opus_repacketizer_get_size());
|
||||||
|
* if (rp != NULL)
|
||||||
|
* opus_repacketizer_init(rp);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Then the application should submit packets with opus_repacketizer_cat(),
|
||||||
|
* extract new packets with opus_repacketizer_out() or
|
||||||
|
* opus_repacketizer_out_range(), and then reset the state for the next set of
|
||||||
|
* input packets via opus_repacketizer_init().
|
||||||
|
*
|
||||||
|
* For example, to split a sequence of packets into individual frames:
|
||||||
|
* @code
|
||||||
|
* unsigned char *data;
|
||||||
|
* int len;
|
||||||
|
* while (get_next_packet(&data, &len))
|
||||||
|
* {
|
||||||
|
* unsigned char out[1276];
|
||||||
|
* opus_int32 out_len;
|
||||||
|
* int nb_frames;
|
||||||
|
* int err;
|
||||||
|
* int i;
|
||||||
|
* err = opus_repacketizer_cat(rp, data, len);
|
||||||
|
* if (err != OPUS_OK)
|
||||||
|
* {
|
||||||
|
* release_packet(data);
|
||||||
|
* return err;
|
||||||
|
* }
|
||||||
|
* nb_frames = opus_repacketizer_get_nb_frames(rp);
|
||||||
|
* for (i = 0; i < nb_frames; i++)
|
||||||
|
* {
|
||||||
|
* out_len = opus_repacketizer_out_range(rp, i, i+1, out, sizeof(out));
|
||||||
|
* if (out_len < 0)
|
||||||
|
* {
|
||||||
|
* release_packet(data);
|
||||||
|
* return (int)out_len;
|
||||||
|
* }
|
||||||
|
* output_next_packet(out, out_len);
|
||||||
|
* }
|
||||||
|
* opus_repacketizer_init(rp);
|
||||||
|
* release_packet(data);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Alternatively, to combine a sequence of frames into packets that each
|
||||||
|
* contain up to <code>TARGET_DURATION_MS</code> milliseconds of data:
|
||||||
|
* @code
|
||||||
|
* // The maximum number of packets with duration TARGET_DURATION_MS occurs
|
||||||
|
* // when the frame size is 2.5 ms, for a total of (TARGET_DURATION_MS*2/5)
|
||||||
|
* // packets.
|
||||||
|
* unsigned char *data[(TARGET_DURATION_MS*2/5)+1];
|
||||||
|
* opus_int32 len[(TARGET_DURATION_MS*2/5)+1];
|
||||||
|
* int nb_packets;
|
||||||
|
* unsigned char out[1277*(TARGET_DURATION_MS*2/2)];
|
||||||
|
* opus_int32 out_len;
|
||||||
|
* int prev_toc;
|
||||||
|
* nb_packets = 0;
|
||||||
|
* while (get_next_packet(data+nb_packets, len+nb_packets))
|
||||||
|
* {
|
||||||
|
* int nb_frames;
|
||||||
|
* int err;
|
||||||
|
* nb_frames = opus_packet_get_nb_frames(data[nb_packets], len[nb_packets]);
|
||||||
|
* if (nb_frames < 1)
|
||||||
|
* {
|
||||||
|
* release_packets(data, nb_packets+1);
|
||||||
|
* return nb_frames;
|
||||||
|
* }
|
||||||
|
* nb_frames += opus_repacketizer_get_nb_frames(rp);
|
||||||
|
* // If adding the next packet would exceed our target, or it has an
|
||||||
|
* // incompatible TOC sequence, output the packets we already have before
|
||||||
|
* // submitting it.
|
||||||
|
* // N.B., The nb_packets > 0 check ensures we've submitted at least one
|
||||||
|
* // packet since the last call to opus_repacketizer_init(). Otherwise a
|
||||||
|
* // single packet longer than TARGET_DURATION_MS would cause us to try to
|
||||||
|
* // output an (invalid) empty packet. It also ensures that prev_toc has
|
||||||
|
* // been set to a valid value. Additionally, len[nb_packets] > 0 is
|
||||||
|
* // guaranteed by the call to opus_packet_get_nb_frames() above, so the
|
||||||
|
* // reference to data[nb_packets][0] should be valid.
|
||||||
|
* if (nb_packets > 0 && (
|
||||||
|
* ((prev_toc & 0xFC) != (data[nb_packets][0] & 0xFC)) ||
|
||||||
|
* opus_packet_get_samples_per_frame(data[nb_packets], 48000)*nb_frames >
|
||||||
|
* TARGET_DURATION_MS*48))
|
||||||
|
* {
|
||||||
|
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
||||||
|
* if (out_len < 0)
|
||||||
|
* {
|
||||||
|
* release_packets(data, nb_packets+1);
|
||||||
|
* return (int)out_len;
|
||||||
|
* }
|
||||||
|
* output_next_packet(out, out_len);
|
||||||
|
* opus_repacketizer_init(rp);
|
||||||
|
* release_packets(data, nb_packets);
|
||||||
|
* data[0] = data[nb_packets];
|
||||||
|
* len[0] = len[nb_packets];
|
||||||
|
* nb_packets = 0;
|
||||||
|
* }
|
||||||
|
* err = opus_repacketizer_cat(rp, data[nb_packets], len[nb_packets]);
|
||||||
|
* if (err != OPUS_OK)
|
||||||
|
* {
|
||||||
|
* release_packets(data, nb_packets+1);
|
||||||
|
* return err;
|
||||||
|
* }
|
||||||
|
* prev_toc = data[nb_packets][0];
|
||||||
|
* nb_packets++;
|
||||||
|
* }
|
||||||
|
* // Output the final, partial packet.
|
||||||
|
* if (nb_packets > 0)
|
||||||
|
* {
|
||||||
|
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
||||||
|
* release_packets(data, nb_packets);
|
||||||
|
* if (out_len < 0)
|
||||||
|
* return (int)out_len;
|
||||||
|
* output_next_packet(out, out_len);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* An alternate way of merging packets is to simply call opus_repacketizer_cat()
|
||||||
|
* unconditionally until it fails. At that point, the merged packet can be
|
||||||
|
* obtained with opus_repacketizer_out() and the input packet for which
|
||||||
|
* opus_repacketizer_cat() needs to be re-added to a newly reinitialized
|
||||||
|
* repacketizer state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct OpusRepacketizer OpusRepacketizer;
|
||||||
|
|
||||||
|
/** Gets the size of an <code>OpusRepacketizer</code> structure.
|
||||||
|
* @returns The size in bytes.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_size(void);
|
||||||
|
|
||||||
|
/** (Re)initializes a previously allocated repacketizer state.
|
||||||
|
* The state must be at least the size returned by opus_repacketizer_get_size().
|
||||||
|
* This can be used for applications which use their own allocator instead of
|
||||||
|
* malloc().
|
||||||
|
* It must also be called to reset the queue of packets waiting to be
|
||||||
|
* repacketized, which is necessary if the maximum packet duration of 120 ms
|
||||||
|
* is reached or if you wish to submit packets with a different Opus
|
||||||
|
* configuration (coding mode, audio bandwidth, frame size, or channel count).
|
||||||
|
* Failure to do so will prevent a new packet from being added with
|
||||||
|
* opus_repacketizer_cat().
|
||||||
|
* @see opus_repacketizer_create
|
||||||
|
* @see opus_repacketizer_get_size
|
||||||
|
* @see opus_repacketizer_cat
|
||||||
|
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to
|
||||||
|
* (re)initialize.
|
||||||
|
* @returns A pointer to the same repacketizer state that was passed in.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OpusRepacketizer *opus_repacketizer_init(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Allocates memory and initializes the new repacketizer with
|
||||||
|
* opus_repacketizer_init().
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusRepacketizer *opus_repacketizer_create(void);
|
||||||
|
|
||||||
|
/** Frees an <code>OpusRepacketizer</code> allocated by
|
||||||
|
* opus_repacketizer_create().
|
||||||
|
* @param[in] rp <tt>OpusRepacketizer*</tt>: State to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_repacketizer_destroy(OpusRepacketizer *rp);
|
||||||
|
|
||||||
|
/** Add a packet to the current repacketizer state.
|
||||||
|
* This packet must match the configuration of any packets already submitted
|
||||||
|
* for repacketization since the last call to opus_repacketizer_init().
|
||||||
|
* This means that it must have the same coding mode, audio bandwidth, frame
|
||||||
|
* size, and channel count.
|
||||||
|
* This can be checked in advance by examining the top 6 bits of the first
|
||||||
|
* byte of the packet, and ensuring they match the top 6 bits of the first
|
||||||
|
* byte of any previously submitted packet.
|
||||||
|
* The total duration of audio in the repacketizer state also must not exceed
|
||||||
|
* 120 ms, the maximum duration of a single packet, after adding this packet.
|
||||||
|
*
|
||||||
|
* The contents of the current repacketizer state can be extracted into new
|
||||||
|
* packets using opus_repacketizer_out() or opus_repacketizer_out_range().
|
||||||
|
*
|
||||||
|
* In order to add a packet with a different configuration or to add more
|
||||||
|
* audio beyond 120 ms, you must clear the repacketizer state by calling
|
||||||
|
* opus_repacketizer_init().
|
||||||
|
* If a packet is too large to add to the current repacketizer state, no part
|
||||||
|
* of it is added, even if it contains multiple frames, some of which might
|
||||||
|
* fit.
|
||||||
|
* If you wish to be able to add parts of such packets, you should first use
|
||||||
|
* another repacketizer to split the packet into pieces and add them
|
||||||
|
* individually.
|
||||||
|
* @see opus_repacketizer_out_range
|
||||||
|
* @see opus_repacketizer_out
|
||||||
|
* @see opus_repacketizer_init
|
||||||
|
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to which to
|
||||||
|
* add the packet.
|
||||||
|
* @param[in] data <tt>const unsigned char*</tt>: The packet data.
|
||||||
|
* The application must ensure
|
||||||
|
* this pointer remains valid
|
||||||
|
* until the next call to
|
||||||
|
* opus_repacketizer_init() or
|
||||||
|
* opus_repacketizer_destroy().
|
||||||
|
* @param len <tt>opus_int32</tt>: The number of bytes in the packet data.
|
||||||
|
* @returns An error code indicating whether or not the operation succeeded.
|
||||||
|
* @retval #OPUS_OK The packet's contents have been added to the repacketizer
|
||||||
|
* state.
|
||||||
|
* @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence,
|
||||||
|
* the packet's TOC sequence was not compatible
|
||||||
|
* with previously submitted packets (because
|
||||||
|
* the coding mode, audio bandwidth, frame size,
|
||||||
|
* or channel count did not match), or adding
|
||||||
|
* this packet would increase the total amount of
|
||||||
|
* audio stored in the repacketizer state to more
|
||||||
|
* than 120 ms.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_repacketizer_cat(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||||
|
|
||||||
|
|
||||||
|
/** Construct a new packet from data previously submitted to the repacketizer
|
||||||
|
* state via opus_repacketizer_cat().
|
||||||
|
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||||
|
* construct the new packet.
|
||||||
|
* @param begin <tt>int</tt>: The index of the first frame in the current
|
||||||
|
* repacketizer state to include in the output.
|
||||||
|
* @param end <tt>int</tt>: One past the index of the last frame in the
|
||||||
|
* current repacketizer state to include in the
|
||||||
|
* output.
|
||||||
|
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||||
|
* store the output packet.
|
||||||
|
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||||
|
* the output buffer. In order to guarantee
|
||||||
|
* success, this should be at least
|
||||||
|
* <code>1276</code> for a single frame,
|
||||||
|
* or for multiple frames,
|
||||||
|
* <code>1277*(end-begin)</code>.
|
||||||
|
* However, <code>1*(end-begin)</code> plus
|
||||||
|
* the size of all packet data submitted to
|
||||||
|
* the repacketizer since the last call to
|
||||||
|
* opus_repacketizer_init() or
|
||||||
|
* opus_repacketizer_create() is also
|
||||||
|
* sufficient, and possibly much smaller.
|
||||||
|
* @returns The total size of the output packet on success, or an error code
|
||||||
|
* on failure.
|
||||||
|
* @retval #OPUS_BAD_ARG <code>[begin,end)</code> was an invalid range of
|
||||||
|
* frames (begin < 0, begin >= end, or end >
|
||||||
|
* opus_repacketizer_get_nb_frames()).
|
||||||
|
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
||||||
|
* complete output packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out_range(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Return the total number of frames contained in packet data submitted to
|
||||||
|
* the repacketizer state so far via opus_repacketizer_cat() since the last
|
||||||
|
* call to opus_repacketizer_init() or opus_repacketizer_create().
|
||||||
|
* This defines the valid range of packets that can be extracted with
|
||||||
|
* opus_repacketizer_out_range() or opus_repacketizer_out().
|
||||||
|
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state containing the
|
||||||
|
* frames.
|
||||||
|
* @returns The total number of frames contained in the packet data submitted
|
||||||
|
* to the repacketizer state.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_nb_frames(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Construct a new packet from data previously submitted to the repacketizer
|
||||||
|
* state via opus_repacketizer_cat().
|
||||||
|
* This is a convenience routine that returns all the data submitted so far
|
||||||
|
* in a single packet.
|
||||||
|
* It is equivalent to calling
|
||||||
|
* @code
|
||||||
|
* opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp),
|
||||||
|
* data, maxlen)
|
||||||
|
* @endcode
|
||||||
|
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
||||||
|
* construct the new packet.
|
||||||
|
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
||||||
|
* store the output packet.
|
||||||
|
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
||||||
|
* the output buffer. In order to guarantee
|
||||||
|
* success, this should be at least
|
||||||
|
* <code>1277*opus_repacketizer_get_nb_frames(rp)</code>.
|
||||||
|
* However,
|
||||||
|
* <code>1*opus_repacketizer_get_nb_frames(rp)</code>
|
||||||
|
* plus the size of all packet data
|
||||||
|
* submitted to the repacketizer since the
|
||||||
|
* last call to opus_repacketizer_init() or
|
||||||
|
* opus_repacketizer_create() is also
|
||||||
|
* sufficient, and possibly much smaller.
|
||||||
|
* @returns The total size of the output packet on success, or an error code
|
||||||
|
* on failure.
|
||||||
|
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
||||||
|
* complete output packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out(OpusRepacketizer *rp, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Pads a given Opus packet to a larger size (possibly changing the TOC sequence).
|
||||||
|
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||||
|
* packet to pad.
|
||||||
|
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
||||||
|
* This must be at least as large as len.
|
||||||
|
* @returns an error code
|
||||||
|
* @retval #OPUS_OK \a on success.
|
||||||
|
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
||||||
|
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len);
|
||||||
|
|
||||||
|
/** Remove all padding from a given Opus packet and rewrite the TOC sequence to
|
||||||
|
* minimize space usage.
|
||||||
|
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||||
|
* packet to strip.
|
||||||
|
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @returns The new size of the output packet on success, or an error code
|
||||||
|
* on failure.
|
||||||
|
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
||||||
|
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len);
|
||||||
|
|
||||||
|
/** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence).
|
||||||
|
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||||
|
* packet to pad.
|
||||||
|
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
||||||
|
* This must be at least as large as len.
|
||||||
|
* @returns an error code
|
||||||
|
* @retval #OPUS_OK \a on success.
|
||||||
|
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
||||||
|
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_multistream_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams);
|
||||||
|
|
||||||
|
/** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to
|
||||||
|
* minimize space usage.
|
||||||
|
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
||||||
|
* packet to strip.
|
||||||
|
* @param len <tt>opus_int32</tt>: The size of the packet.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
||||||
|
* This must be at least 1.
|
||||||
|
* @returns The new size of the output packet on success, or an error code
|
||||||
|
* on failure.
|
||||||
|
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
||||||
|
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, int nb_streams);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_H */
|
||||||
342
app/src/main/cpp/opus_custom.h
Normal file
342
app/src/main/cpp/opus_custom.h
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
/* Copyright (c) 2007-2008 CSIRO
|
||||||
|
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||||||
|
Copyright (c) 2008-2012 Gregory Maxwell
|
||||||
|
Written by Jean-Marc Valin and Gregory Maxwell */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file opus_custom.h
|
||||||
|
@brief Opus-Custom reference implementation API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPUS_CUSTOM_H
|
||||||
|
#define OPUS_CUSTOM_H
|
||||||
|
|
||||||
|
#include "opus_defines.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CUSTOM_MODES
|
||||||
|
# define OPUS_CUSTOM_EXPORT OPUS_EXPORT
|
||||||
|
# define OPUS_CUSTOM_EXPORT_STATIC OPUS_EXPORT
|
||||||
|
#else
|
||||||
|
# define OPUS_CUSTOM_EXPORT
|
||||||
|
# ifdef OPUS_BUILD
|
||||||
|
# define OPUS_CUSTOM_EXPORT_STATIC static OPUS_INLINE
|
||||||
|
# else
|
||||||
|
# define OPUS_CUSTOM_EXPORT_STATIC
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @defgroup opus_custom Opus Custom
|
||||||
|
* @{
|
||||||
|
* Opus Custom is an optional part of the Opus specification and
|
||||||
|
* reference implementation which uses a distinct API from the regular
|
||||||
|
* API and supports frame sizes that are not normally supported.\ Use
|
||||||
|
* of Opus Custom is discouraged for all but very special applications
|
||||||
|
* for which a frame size different from 2.5, 5, 10, or 20 ms is needed
|
||||||
|
* (for either complexity or latency reasons) and where interoperability
|
||||||
|
* is less important.
|
||||||
|
*
|
||||||
|
* In addition to the interoperability limitations the use of Opus custom
|
||||||
|
* disables a substantial chunk of the codec and generally lowers the
|
||||||
|
* quality available at a given bitrate. Normally when an application needs
|
||||||
|
* a different frame size from the codec it should buffer to match the
|
||||||
|
* sizes but this adds a small amount of delay which may be important
|
||||||
|
* in some very low latency applications. Some transports (especially
|
||||||
|
* constant rate RF transports) may also work best with frames of
|
||||||
|
* particular durations.
|
||||||
|
*
|
||||||
|
* Libopus only supports custom modes if they are enabled at compile time.
|
||||||
|
*
|
||||||
|
* The Opus Custom API is similar to the regular API but the
|
||||||
|
* @ref opus_encoder_create and @ref opus_decoder_create calls take
|
||||||
|
* an additional mode parameter which is a structure produced by
|
||||||
|
* a call to @ref opus_custom_mode_create. Both the encoder and decoder
|
||||||
|
* must create a mode using the same sample rate (fs) and frame size
|
||||||
|
* (frame size) so these parameters must either be signaled out of band
|
||||||
|
* or fixed in a particular implementation.
|
||||||
|
*
|
||||||
|
* Similar to regular Opus the custom modes support on the fly frame size
|
||||||
|
* switching, but the sizes available depend on the particular frame size in
|
||||||
|
* use. For some initial frame sizes on a single on the fly size is available.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Contains the state of an encoder. One encoder state is needed
|
||||||
|
for each stream. It is initialized once at the beginning of the
|
||||||
|
stream. Do *not* re-initialize the state for every frame.
|
||||||
|
@brief Encoder state
|
||||||
|
*/
|
||||||
|
typedef struct OpusCustomEncoder OpusCustomEncoder;
|
||||||
|
|
||||||
|
/** State of the decoder. One decoder state is needed for each stream.
|
||||||
|
It is initialized once at the beginning of the stream. Do *not*
|
||||||
|
re-initialize the state for every frame.
|
||||||
|
@brief Decoder state
|
||||||
|
*/
|
||||||
|
typedef struct OpusCustomDecoder OpusCustomDecoder;
|
||||||
|
|
||||||
|
/** The mode contains all the information necessary to create an
|
||||||
|
encoder. Both the encoder and decoder need to be initialized
|
||||||
|
with exactly the same mode, otherwise the output will be
|
||||||
|
corrupted.
|
||||||
|
@brief Mode configuration
|
||||||
|
*/
|
||||||
|
typedef struct OpusCustomMode OpusCustomMode;
|
||||||
|
|
||||||
|
/** Creates a new mode struct. This will be passed to an encoder or
|
||||||
|
* decoder. The mode MUST NOT BE DESTROYED until the encoders and
|
||||||
|
* decoders that use it are destroyed as well.
|
||||||
|
* @param [in] Fs <tt>int</tt>: Sampling rate (8000 to 96000 Hz)
|
||||||
|
* @param [in] frame_size <tt>int</tt>: Number of samples (per channel) to encode in each
|
||||||
|
* packet (64 - 1024, prime factorization must contain zero or more 2s, 3s, or 5s and no other primes)
|
||||||
|
* @param [out] error <tt>int*</tt>: Returned error code (if NULL, no error will be returned)
|
||||||
|
* @return A newly created mode
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomMode *opus_custom_mode_create(opus_int32 Fs, int frame_size, int *error);
|
||||||
|
|
||||||
|
/** Destroys a mode struct. Only call this after all encoders and
|
||||||
|
* decoders using this mode are destroyed as well.
|
||||||
|
* @param [in] mode <tt>OpusCustomMode*</tt>: Mode to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT void opus_custom_mode_destroy(OpusCustomMode *mode);
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(OPUS_BUILD) || defined(CELT_ENCODER_C)
|
||||||
|
|
||||||
|
/* Encoder */
|
||||||
|
/** Gets the size of an OpusCustomEncoder structure.
|
||||||
|
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @returns size
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_encoder_get_size(
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
# ifdef CUSTOM_MODES
|
||||||
|
/** Initializes a previously allocated encoder state
|
||||||
|
* The memory pointed to by st must be the size returned by opus_custom_encoder_get_size.
|
||||||
|
* This is intended for applications which use their own allocator instead of malloc.
|
||||||
|
* @see opus_custom_encoder_create(),opus_custom_encoder_get_size()
|
||||||
|
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
||||||
|
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
||||||
|
* the stream (must be the same characteristics as used for the
|
||||||
|
* decoder)
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @return OPUS_OK Success or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT int opus_custom_encoder_init(
|
||||||
|
OpusCustomEncoder *st,
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new encoder state. Each stream needs its own encoder
|
||||||
|
* state (can't be shared across simultaneous streams).
|
||||||
|
* @param [in] mode <tt>OpusCustomMode*</tt>: Contains all the information about the characteristics of
|
||||||
|
* the stream (must be the same characteristics as used for the
|
||||||
|
* decoder)
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @param [out] error <tt>int*</tt>: Returns an error code
|
||||||
|
* @return Newly created encoder state.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomEncoder *opus_custom_encoder_create(
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
|
||||||
|
/** Destroys a an encoder state.
|
||||||
|
* @param[in] st <tt>OpusCustomEncoder*</tt>: State to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT void opus_custom_encoder_destroy(OpusCustomEncoder *st);
|
||||||
|
|
||||||
|
/** Encodes a frame of audio.
|
||||||
|
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] pcm <tt>float*</tt>: PCM audio in float format, with a normal range of +/-1.0.
|
||||||
|
* Samples with a range beyond +/-1.0 are supported but will
|
||||||
|
* be clipped by decoders using the integer API and should
|
||||||
|
* only be used if it is known that the far end supports
|
||||||
|
* extended dynamic range. There must be exactly
|
||||||
|
* frame_size samples per channel.
|
||||||
|
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
||||||
|
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
||||||
|
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
||||||
|
* (can change from one frame to another)
|
||||||
|
* @return Number of bytes written to "compressed".
|
||||||
|
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||||
|
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||||
|
* decoding is possible.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode_float(
|
||||||
|
OpusCustomEncoder *st,
|
||||||
|
const float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *compressed,
|
||||||
|
int maxCompressedBytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Encodes a frame of audio.
|
||||||
|
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
||||||
|
* @param [in] pcm <tt>opus_int16*</tt>: PCM audio in signed 16-bit format (native endian).
|
||||||
|
* There must be exactly frame_size samples per channel.
|
||||||
|
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
||||||
|
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
||||||
|
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
||||||
|
* (can change from one frame to another)
|
||||||
|
* @return Number of bytes written to "compressed".
|
||||||
|
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||||
|
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||||
|
* decoding is possible.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode(
|
||||||
|
OpusCustomEncoder *st,
|
||||||
|
const opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *compressed,
|
||||||
|
int maxCompressedBytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Perform a CTL function on an Opus custom encoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated
|
||||||
|
* by a convenience macro.
|
||||||
|
* @see opus_encoderctls
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT int opus_custom_encoder_ctl(OpusCustomEncoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(OPUS_BUILD) || defined(CELT_DECODER_C)
|
||||||
|
/* Decoder */
|
||||||
|
|
||||||
|
/** Gets the size of an OpusCustomDecoder structure.
|
||||||
|
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @returns size
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_decoder_get_size(
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Initializes a previously allocated decoder state
|
||||||
|
* The memory pointed to by st must be the size returned by opus_custom_decoder_get_size.
|
||||||
|
* This is intended for applications which use their own allocator instead of malloc.
|
||||||
|
* @see opus_custom_decoder_create(),opus_custom_decoder_get_size()
|
||||||
|
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
||||||
|
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
||||||
|
* the stream (must be the same characteristics as used for the
|
||||||
|
* encoder)
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @return OPUS_OK Success or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT_STATIC int opus_custom_decoder_init(
|
||||||
|
OpusCustomDecoder *st,
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a new decoder state. Each stream needs its own decoder state (can't
|
||||||
|
* be shared across simultaneous streams).
|
||||||
|
* @param [in] mode <tt>OpusCustomMode</tt>: Contains all the information about the characteristics of the
|
||||||
|
* stream (must be the same characteristics as used for the encoder)
|
||||||
|
* @param [in] channels <tt>int</tt>: Number of channels
|
||||||
|
* @param [out] error <tt>int*</tt>: Returns an error code
|
||||||
|
* @return Newly created decoder state.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomDecoder *opus_custom_decoder_create(
|
||||||
|
const OpusCustomMode *mode,
|
||||||
|
int channels,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Destroys a an decoder state.
|
||||||
|
* @param[in] st <tt>OpusCustomDecoder*</tt>: State to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT void opus_custom_decoder_destroy(OpusCustomDecoder *st);
|
||||||
|
|
||||||
|
/** Decode an opus custom frame with floating point output
|
||||||
|
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||||
|
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
||||||
|
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
||||||
|
* is frame_size*channels*sizeof(float)
|
||||||
|
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
||||||
|
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode_float(
|
||||||
|
OpusCustomDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
int len,
|
||||||
|
float *pcm,
|
||||||
|
int frame_size
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Decode an opus custom frame
|
||||||
|
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
||||||
|
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
||||||
|
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
||||||
|
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
||||||
|
* is frame_size*channels*sizeof(opus_int16)
|
||||||
|
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
||||||
|
* @returns Number of decoded samples or @ref opus_errorcodes
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode(
|
||||||
|
OpusCustomDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
int len,
|
||||||
|
opus_int16 *pcm,
|
||||||
|
int frame_size
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Perform a CTL function on an Opus custom decoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated
|
||||||
|
* by a convenience macro.
|
||||||
|
* @see opus_genericctls
|
||||||
|
*/
|
||||||
|
OPUS_CUSTOM_EXPORT int opus_custom_decoder_ctl(OpusCustomDecoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_CUSTOM_H */
|
||||||
70
app/src/main/cpp/opus_decoder.cpp
Normal file
70
app/src/main/cpp/opus_decoder.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <string>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include "opus.h"
|
||||||
|
|
||||||
|
#define LOG_TAG "OpusJNI"
|
||||||
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||||
|
|
||||||
|
static OpusDecoder* decoderHandle = nullptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusDecoder_nativeInitDecoder(JNIEnv *env, jobject thiz,
|
||||||
|
jint sample_rate, jint channels) {
|
||||||
|
int error;
|
||||||
|
OpusDecoder *decoder = opus_decoder_create(sample_rate, channels, &error);
|
||||||
|
|
||||||
|
if (error != OPUS_OK || decoder == nullptr) {
|
||||||
|
LOGE("Failed to create decoder: %s", opus_strerror(error));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGI("Opus decoder initialized: sample_rate=%d, channels=%d", sample_rate, channels);
|
||||||
|
return (jlong)(intptr_t)decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusDecoder_nativeDecodeBytes(JNIEnv *env, jobject thiz,
|
||||||
|
jlong decoder_handle,
|
||||||
|
jbyteArray input_buffer,
|
||||||
|
jint input_size,
|
||||||
|
jbyteArray output_buffer,
|
||||||
|
jint max_output_size) {
|
||||||
|
OpusDecoder *decoder = (OpusDecoder*)(intptr_t)decoder_handle;
|
||||||
|
if (decoder == nullptr) {
|
||||||
|
LOGE("Decoder handle is null");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyte *input = env->GetByteArrayElements(input_buffer, nullptr);
|
||||||
|
jbyte *output = env->GetByteArrayElements(output_buffer, nullptr);
|
||||||
|
|
||||||
|
int frame_size = max_output_size / 2; // 16-bit PCM
|
||||||
|
int result = opus_decode(decoder, (unsigned char*)input, input_size,
|
||||||
|
(opus_int16*)output, frame_size, 0);
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(input_buffer, input, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(output_buffer, output, 0);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
LOGE("Decoding failed: %s", opus_strerror(result));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result * 2; // 返回字节数(每个样本2字节)
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusDecoder_nativeReleaseDecoder(JNIEnv *env, jobject thiz,
|
||||||
|
jlong decoder_handle) {
|
||||||
|
OpusDecoder *decoder = (OpusDecoder*)(intptr_t)decoder_handle;
|
||||||
|
if (decoder != nullptr) {
|
||||||
|
opus_decoder_destroy(decoder);
|
||||||
|
LOGI("Opus decoder released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
799
app/src/main/cpp/opus_defines.h
Normal file
799
app/src/main/cpp/opus_defines.h
Normal file
@ -0,0 +1,799 @@
|
|||||||
|
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
||||||
|
Written by Jean-Marc Valin and Koen Vos */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file opus_defines.h
|
||||||
|
* @brief Opus reference implementation constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPUS_DEFINES_H
|
||||||
|
#define OPUS_DEFINES_H
|
||||||
|
|
||||||
|
#include "opus_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @defgroup opus_errorcodes Error codes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/** No error @hideinitializer*/
|
||||||
|
#define OPUS_OK 0
|
||||||
|
/** One or more invalid/out of range arguments @hideinitializer*/
|
||||||
|
#define OPUS_BAD_ARG -1
|
||||||
|
/** Not enough bytes allocated in the buffer @hideinitializer*/
|
||||||
|
#define OPUS_BUFFER_TOO_SMALL -2
|
||||||
|
/** An internal error was detected @hideinitializer*/
|
||||||
|
#define OPUS_INTERNAL_ERROR -3
|
||||||
|
/** The compressed data passed is corrupted @hideinitializer*/
|
||||||
|
#define OPUS_INVALID_PACKET -4
|
||||||
|
/** Invalid/unsupported request number @hideinitializer*/
|
||||||
|
#define OPUS_UNIMPLEMENTED -5
|
||||||
|
/** An encoder or decoder structure is invalid or already freed @hideinitializer*/
|
||||||
|
#define OPUS_INVALID_STATE -6
|
||||||
|
/** Memory allocation has failed @hideinitializer*/
|
||||||
|
#define OPUS_ALLOC_FAIL -7
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @cond OPUS_INTERNAL_DOC */
|
||||||
|
/**Export control for opus functions */
|
||||||
|
|
||||||
|
#ifndef OPUS_EXPORT
|
||||||
|
# if defined(WIN32)
|
||||||
|
# if defined(OPUS_BUILD) && defined(DLL_EXPORT)
|
||||||
|
# define OPUS_EXPORT __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define OPUS_EXPORT
|
||||||
|
# endif
|
||||||
|
# elif defined(__GNUC__) && defined(OPUS_BUILD)
|
||||||
|
# define OPUS_EXPORT __attribute__ ((visibility ("default")))
|
||||||
|
# else
|
||||||
|
# define OPUS_EXPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
# if !defined(OPUS_GNUC_PREREQ)
|
||||||
|
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
||||||
|
# define OPUS_GNUC_PREREQ(_maj,_min) \
|
||||||
|
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
||||||
|
# else
|
||||||
|
# define OPUS_GNUC_PREREQ(_maj,_min) 0
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
||||||
|
# if OPUS_GNUC_PREREQ(3,0)
|
||||||
|
# define OPUS_RESTRICT __restrict__
|
||||||
|
# elif (defined(_MSC_VER) && _MSC_VER >= 1400)
|
||||||
|
# define OPUS_RESTRICT __restrict
|
||||||
|
# else
|
||||||
|
# define OPUS_RESTRICT
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define OPUS_RESTRICT restrict
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
||||||
|
# if OPUS_GNUC_PREREQ(2,7)
|
||||||
|
# define OPUS_INLINE __inline__
|
||||||
|
# elif (defined(_MSC_VER))
|
||||||
|
# define OPUS_INLINE __inline
|
||||||
|
# else
|
||||||
|
# define OPUS_INLINE
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define OPUS_INLINE inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**Warning attributes for opus functions
|
||||||
|
* NONNULL is not used in OPUS_BUILD to avoid the compiler optimizing out
|
||||||
|
* some paranoid null checks. */
|
||||||
|
#if defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
||||||
|
# define OPUS_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
|
||||||
|
#else
|
||||||
|
# define OPUS_WARN_UNUSED_RESULT
|
||||||
|
#endif
|
||||||
|
#if !defined(OPUS_BUILD) && defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
||||||
|
# define OPUS_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
|
||||||
|
#else
|
||||||
|
# define OPUS_ARG_NONNULL(_x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** These are the actual Encoder CTL ID numbers.
|
||||||
|
* They should not be used directly by applications.
|
||||||
|
* In general, SETs should be even and GETs should be odd.*/
|
||||||
|
#define OPUS_SET_APPLICATION_REQUEST 4000
|
||||||
|
#define OPUS_GET_APPLICATION_REQUEST 4001
|
||||||
|
#define OPUS_SET_BITRATE_REQUEST 4002
|
||||||
|
#define OPUS_GET_BITRATE_REQUEST 4003
|
||||||
|
#define OPUS_SET_MAX_BANDWIDTH_REQUEST 4004
|
||||||
|
#define OPUS_GET_MAX_BANDWIDTH_REQUEST 4005
|
||||||
|
#define OPUS_SET_VBR_REQUEST 4006
|
||||||
|
#define OPUS_GET_VBR_REQUEST 4007
|
||||||
|
#define OPUS_SET_BANDWIDTH_REQUEST 4008
|
||||||
|
#define OPUS_GET_BANDWIDTH_REQUEST 4009
|
||||||
|
#define OPUS_SET_COMPLEXITY_REQUEST 4010
|
||||||
|
#define OPUS_GET_COMPLEXITY_REQUEST 4011
|
||||||
|
#define OPUS_SET_INBAND_FEC_REQUEST 4012
|
||||||
|
#define OPUS_GET_INBAND_FEC_REQUEST 4013
|
||||||
|
#define OPUS_SET_PACKET_LOSS_PERC_REQUEST 4014
|
||||||
|
#define OPUS_GET_PACKET_LOSS_PERC_REQUEST 4015
|
||||||
|
#define OPUS_SET_DTX_REQUEST 4016
|
||||||
|
#define OPUS_GET_DTX_REQUEST 4017
|
||||||
|
#define OPUS_SET_VBR_CONSTRAINT_REQUEST 4020
|
||||||
|
#define OPUS_GET_VBR_CONSTRAINT_REQUEST 4021
|
||||||
|
#define OPUS_SET_FORCE_CHANNELS_REQUEST 4022
|
||||||
|
#define OPUS_GET_FORCE_CHANNELS_REQUEST 4023
|
||||||
|
#define OPUS_SET_SIGNAL_REQUEST 4024
|
||||||
|
#define OPUS_GET_SIGNAL_REQUEST 4025
|
||||||
|
#define OPUS_GET_LOOKAHEAD_REQUEST 4027
|
||||||
|
/* #define OPUS_RESET_STATE 4028 */
|
||||||
|
#define OPUS_GET_SAMPLE_RATE_REQUEST 4029
|
||||||
|
#define OPUS_GET_FINAL_RANGE_REQUEST 4031
|
||||||
|
#define OPUS_GET_PITCH_REQUEST 4033
|
||||||
|
#define OPUS_SET_GAIN_REQUEST 4034
|
||||||
|
#define OPUS_GET_GAIN_REQUEST 4045 /* Should have been 4035 */
|
||||||
|
#define OPUS_SET_LSB_DEPTH_REQUEST 4036
|
||||||
|
#define OPUS_GET_LSB_DEPTH_REQUEST 4037
|
||||||
|
#define OPUS_GET_LAST_PACKET_DURATION_REQUEST 4039
|
||||||
|
#define OPUS_SET_EXPERT_FRAME_DURATION_REQUEST 4040
|
||||||
|
#define OPUS_GET_EXPERT_FRAME_DURATION_REQUEST 4041
|
||||||
|
#define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042
|
||||||
|
#define OPUS_GET_PREDICTION_DISABLED_REQUEST 4043
|
||||||
|
/* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */
|
||||||
|
#define OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST 4046
|
||||||
|
#define OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST 4047
|
||||||
|
#define OPUS_GET_IN_DTX_REQUEST 4049
|
||||||
|
|
||||||
|
/** Defines for the presence of extended APIs. */
|
||||||
|
#define OPUS_HAVE_OPUS_PROJECTION_H
|
||||||
|
|
||||||
|
/* Macros to trigger compilation errors when the wrong types are provided to a CTL */
|
||||||
|
#define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
|
||||||
|
#define __opus_check_int_ptr(ptr) ((ptr) + ((ptr) - (opus_int32*)(ptr)))
|
||||||
|
#define __opus_check_uint_ptr(ptr) ((ptr) + ((ptr) - (opus_uint32*)(ptr)))
|
||||||
|
#define __opus_check_val16_ptr(ptr) ((ptr) + ((ptr) - (opus_val16*)(ptr)))
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/** @defgroup opus_ctlvalues Pre-defined values for CTL interface
|
||||||
|
* @see opus_genericctls, opus_encoderctls
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Values for the various encoder CTLs */
|
||||||
|
#define OPUS_AUTO -1000 /**<Auto/default setting @hideinitializer*/
|
||||||
|
#define OPUS_BITRATE_MAX -1 /**<Maximum bitrate @hideinitializer*/
|
||||||
|
|
||||||
|
/** Best for most VoIP/videoconference applications where listening quality and intelligibility matter most
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_APPLICATION_VOIP 2048
|
||||||
|
/** Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_APPLICATION_AUDIO 2049
|
||||||
|
/** Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_APPLICATION_RESTRICTED_LOWDELAY 2051
|
||||||
|
|
||||||
|
#define OPUS_SIGNAL_VOICE 3001 /**< Signal being encoded is voice */
|
||||||
|
#define OPUS_SIGNAL_MUSIC 3002 /**< Signal being encoded is music */
|
||||||
|
#define OPUS_BANDWIDTH_NARROWBAND 1101 /**< 4 kHz bandpass @hideinitializer*/
|
||||||
|
#define OPUS_BANDWIDTH_MEDIUMBAND 1102 /**< 6 kHz bandpass @hideinitializer*/
|
||||||
|
#define OPUS_BANDWIDTH_WIDEBAND 1103 /**< 8 kHz bandpass @hideinitializer*/
|
||||||
|
#define OPUS_BANDWIDTH_SUPERWIDEBAND 1104 /**<12 kHz bandpass @hideinitializer*/
|
||||||
|
#define OPUS_BANDWIDTH_FULLBAND 1105 /**<20 kHz bandpass @hideinitializer*/
|
||||||
|
|
||||||
|
#define OPUS_FRAMESIZE_ARG 5000 /**< Select frame size from the argument (default) */
|
||||||
|
#define OPUS_FRAMESIZE_2_5_MS 5001 /**< Use 2.5 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_5_MS 5002 /**< Use 5 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_10_MS 5003 /**< Use 10 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_20_MS 5004 /**< Use 20 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_40_MS 5005 /**< Use 40 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_60_MS 5006 /**< Use 60 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_80_MS 5007 /**< Use 80 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_100_MS 5008 /**< Use 100 ms frames */
|
||||||
|
#define OPUS_FRAMESIZE_120_MS 5009 /**< Use 120 ms frames */
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @defgroup opus_encoderctls Encoder related CTLs
|
||||||
|
*
|
||||||
|
* These are convenience macros for use with the \c opus_encode_ctl
|
||||||
|
* interface. They are used to generate the appropriate series of
|
||||||
|
* arguments for that call, passing the correct type, size and so
|
||||||
|
* on as expected for each particular request.
|
||||||
|
*
|
||||||
|
* Some usage examples:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* int ret;
|
||||||
|
* ret = opus_encoder_ctl(enc_ctx, OPUS_SET_BANDWIDTH(OPUS_AUTO));
|
||||||
|
* if (ret != OPUS_OK) return ret;
|
||||||
|
*
|
||||||
|
* opus_int32 rate;
|
||||||
|
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&rate));
|
||||||
|
*
|
||||||
|
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @see opus_genericctls, opus_encoder
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Configures the encoder's computational complexity.
|
||||||
|
* The supported range is 0-10 inclusive with 10 representing the highest complexity.
|
||||||
|
* @see OPUS_GET_COMPLEXITY
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values: 0-10, inclusive.
|
||||||
|
*
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_COMPLEXITY(x) OPUS_SET_COMPLEXITY_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's complexity configuration.
|
||||||
|
* @see OPUS_SET_COMPLEXITY
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns a value in the range 0-10,
|
||||||
|
* inclusive.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_COMPLEXITY(x) OPUS_GET_COMPLEXITY_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the bitrate in the encoder.
|
||||||
|
* Rates from 500 to 512000 bits per second are meaningful, as well as the
|
||||||
|
* special values #OPUS_AUTO and #OPUS_BITRATE_MAX.
|
||||||
|
* The value #OPUS_BITRATE_MAX can be used to cause the codec to use as much
|
||||||
|
* rate as it can, which is useful for controlling the rate by adjusting the
|
||||||
|
* output buffer size.
|
||||||
|
* @see OPUS_GET_BITRATE
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Bitrate in bits per second. The default
|
||||||
|
* is determined based on the number of
|
||||||
|
* channels and the input sampling rate.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_BITRATE(x) OPUS_SET_BITRATE_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's bitrate configuration.
|
||||||
|
* @see OPUS_SET_BITRATE
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns the bitrate in bits per second.
|
||||||
|
* The default is determined based on the
|
||||||
|
* number of channels and the input
|
||||||
|
* sampling rate.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_BITRATE(x) OPUS_GET_BITRATE_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Enables or disables variable bitrate (VBR) in the encoder.
|
||||||
|
* The configured bitrate may not be met exactly because frames must
|
||||||
|
* be an integer number of bytes in length.
|
||||||
|
* @see OPUS_GET_VBR
|
||||||
|
* @see OPUS_SET_VBR_CONSTRAINT
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Hard CBR. For LPC/hybrid modes at very low bit-rate, this can
|
||||||
|
* cause noticeable quality degradation.</dd>
|
||||||
|
* <dt>1</dt><dd>VBR (default). The exact type of VBR is controlled by
|
||||||
|
* #OPUS_SET_VBR_CONSTRAINT.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_VBR(x) OPUS_SET_VBR_REQUEST, __opus_check_int(x)
|
||||||
|
/** Determine if variable bitrate (VBR) is enabled in the encoder.
|
||||||
|
* @see OPUS_SET_VBR
|
||||||
|
* @see OPUS_GET_VBR_CONSTRAINT
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Hard CBR.</dd>
|
||||||
|
* <dt>1</dt><dd>VBR (default). The exact type of VBR may be retrieved via
|
||||||
|
* #OPUS_GET_VBR_CONSTRAINT.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_VBR(x) OPUS_GET_VBR_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Enables or disables constrained VBR in the encoder.
|
||||||
|
* This setting is ignored when the encoder is in CBR mode.
|
||||||
|
* @warning Only the MDCT mode of Opus currently heeds the constraint.
|
||||||
|
* Speech mode ignores it completely, hybrid mode may fail to obey it
|
||||||
|
* if the LPC layer uses more bitrate than the constraint would have
|
||||||
|
* permitted.
|
||||||
|
* @see OPUS_GET_VBR_CONSTRAINT
|
||||||
|
* @see OPUS_SET_VBR
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
||||||
|
* <dt>1</dt><dd>Constrained VBR (default). This creates a maximum of one
|
||||||
|
* frame of buffering delay assuming a transport with a
|
||||||
|
* serialization speed of the nominal bitrate.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_VBR_CONSTRAINT(x) OPUS_SET_VBR_CONSTRAINT_REQUEST, __opus_check_int(x)
|
||||||
|
/** Determine if constrained VBR is enabled in the encoder.
|
||||||
|
* @see OPUS_SET_VBR_CONSTRAINT
|
||||||
|
* @see OPUS_GET_VBR
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
||||||
|
* <dt>1</dt><dd>Constrained VBR (default).</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_VBR_CONSTRAINT(x) OPUS_GET_VBR_CONSTRAINT_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures mono/stereo forcing in the encoder.
|
||||||
|
* This can force the encoder to produce packets encoded as either mono or
|
||||||
|
* stereo, regardless of the format of the input audio. This is useful when
|
||||||
|
* the caller knows that the input signal is currently a mono source embedded
|
||||||
|
* in a stereo stream.
|
||||||
|
* @see OPUS_GET_FORCE_CHANNELS
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
||||||
|
* <dt>1</dt> <dd>Forced mono</dd>
|
||||||
|
* <dt>2</dt> <dd>Forced stereo</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_FORCE_CHANNELS(x) OPUS_SET_FORCE_CHANNELS_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's forced channel configuration.
|
||||||
|
* @see OPUS_SET_FORCE_CHANNELS
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
||||||
|
* <dt>1</dt> <dd>Forced mono</dd>
|
||||||
|
* <dt>2</dt> <dd>Forced stereo</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_FORCE_CHANNELS(x) OPUS_GET_FORCE_CHANNELS_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the maximum bandpass that the encoder will select automatically.
|
||||||
|
* Applications should normally use this instead of #OPUS_SET_BANDWIDTH
|
||||||
|
* (leaving that set to the default, #OPUS_AUTO). This allows the
|
||||||
|
* application to set an upper bound based on the type of input it is
|
||||||
|
* providing, but still gives the encoder the freedom to reduce the bandpass
|
||||||
|
* when the bitrate becomes too low, for better overall quality.
|
||||||
|
* @see OPUS_GET_MAX_BANDWIDTH
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||||
|
* <dt>OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||||
|
* <dt>OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||||
|
* <dt>OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||||
|
* <dt>OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_MAX_BANDWIDTH(x) OPUS_SET_MAX_BANDWIDTH_REQUEST, __opus_check_int(x)
|
||||||
|
|
||||||
|
/** Gets the encoder's configured maximum allowed bandpass.
|
||||||
|
* @see OPUS_SET_MAX_BANDWIDTH
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_MAX_BANDWIDTH(x) OPUS_GET_MAX_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Sets the encoder's bandpass to a specific value.
|
||||||
|
* This prevents the encoder from automatically selecting the bandpass based
|
||||||
|
* on the available bitrate. If an application knows the bandpass of the input
|
||||||
|
* audio it is providing, it should normally use #OPUS_SET_MAX_BANDWIDTH
|
||||||
|
* instead, which still gives the encoder the freedom to reduce the bandpass
|
||||||
|
* when the bitrate becomes too low, for better overall quality.
|
||||||
|
* @see OPUS_GET_BANDWIDTH
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_BANDWIDTH(x) OPUS_SET_BANDWIDTH_REQUEST, __opus_check_int(x)
|
||||||
|
|
||||||
|
/** Configures the type of signal being encoded.
|
||||||
|
* This is a hint which helps the encoder's mode selection.
|
||||||
|
* @see OPUS_GET_SIGNAL
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||||
|
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
||||||
|
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_SIGNAL(x) OPUS_SET_SIGNAL_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured signal type.
|
||||||
|
* @see OPUS_SET_SIGNAL
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||||
|
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
||||||
|
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_SIGNAL(x) OPUS_GET_SIGNAL_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
|
||||||
|
/** Configures the encoder's intended application.
|
||||||
|
* The initial value is a mandatory argument to the encoder_create function.
|
||||||
|
* @see OPUS_GET_APPLICATION
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_APPLICATION(x) OPUS_SET_APPLICATION_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured application.
|
||||||
|
* @see OPUS_SET_APPLICATION
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Gets the total samples of delay added by the entire codec.
|
||||||
|
* This can be queried by the encoder and then the provided number of samples can be
|
||||||
|
* skipped on from the start of the decoder's output to provide time aligned input
|
||||||
|
* and output. From the perspective of a decoding application the real data begins this many
|
||||||
|
* samples late.
|
||||||
|
*
|
||||||
|
* The decoder contribution to this delay is identical for all decoders, but the
|
||||||
|
* encoder portion of the delay may vary from implementation to implementation,
|
||||||
|
* version to version, or even depend on the encoder's initial configuration.
|
||||||
|
* Applications needing delay compensation should call this CTL rather than
|
||||||
|
* hard-coding a value.
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Number of lookahead samples
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_LOOKAHEAD(x) OPUS_GET_LOOKAHEAD_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the encoder's use of inband forward error correction (FEC).
|
||||||
|
* @note This is only applicable to the LPC layer
|
||||||
|
* @see OPUS_GET_INBAND_FEC
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Disable inband FEC (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Enable inband FEC.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_INBAND_FEC(x) OPUS_SET_INBAND_FEC_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets encoder's configured use of inband forward error correction.
|
||||||
|
* @see OPUS_SET_INBAND_FEC
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Inband FEC disabled (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Inband FEC enabled.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_INBAND_FEC(x) OPUS_GET_INBAND_FEC_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the encoder's expected packet loss percentage.
|
||||||
|
* Higher values trigger progressively more loss resistant behavior in the encoder
|
||||||
|
* at the expense of quality at a given bitrate in the absence of packet loss, but
|
||||||
|
* greater quality under loss.
|
||||||
|
* @see OPUS_GET_PACKET_LOSS_PERC
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Loss percentage in the range 0-100, inclusive (default: 0).
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_PACKET_LOSS_PERC(x) OPUS_SET_PACKET_LOSS_PERC_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured packet loss percentage.
|
||||||
|
* @see OPUS_SET_PACKET_LOSS_PERC
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns the configured loss percentage
|
||||||
|
* in the range 0-100, inclusive (default: 0).
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_PACKET_LOSS_PERC(x) OPUS_GET_PACKET_LOSS_PERC_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the encoder's use of discontinuous transmission (DTX).
|
||||||
|
* @note This is only applicable to the LPC layer
|
||||||
|
* @see OPUS_GET_DTX
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Disable DTX (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Enabled DTX.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_DTX(x) OPUS_SET_DTX_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets encoder's configured use of discontinuous transmission.
|
||||||
|
* @see OPUS_SET_DTX
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>DTX disabled (default).</dd>
|
||||||
|
* <dt>1</dt><dd>DTX enabled.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_DTX(x) OPUS_GET_DTX_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
/** Configures the depth of signal being encoded.
|
||||||
|
*
|
||||||
|
* This is a hint which helps the encoder identify silence and near-silence.
|
||||||
|
* It represents the number of significant bits of linear intensity below
|
||||||
|
* which the signal contains ignorable quantization or other noise.
|
||||||
|
*
|
||||||
|
* For example, OPUS_SET_LSB_DEPTH(14) would be an appropriate setting
|
||||||
|
* for G.711 u-law input. OPUS_SET_LSB_DEPTH(16) would be appropriate
|
||||||
|
* for 16-bit linear pcm input with opus_encode_float().
|
||||||
|
*
|
||||||
|
* When using opus_encode() instead of opus_encode_float(), or when libopus
|
||||||
|
* is compiled for fixed-point, the encoder uses the minimum of the value
|
||||||
|
* set here and the value 16.
|
||||||
|
*
|
||||||
|
* @see OPUS_GET_LSB_DEPTH
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Input precision in bits, between 8 and 24
|
||||||
|
* (default: 24).
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_LSB_DEPTH(x) OPUS_SET_LSB_DEPTH_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured signal depth.
|
||||||
|
* @see OPUS_SET_LSB_DEPTH
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Input precision in bits, between 8 and
|
||||||
|
* 24 (default: 24).
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_LSB_DEPTH(x) OPUS_GET_LSB_DEPTH_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Configures the encoder's use of variable duration frames.
|
||||||
|
* When variable duration is enabled, the encoder is free to use a shorter frame
|
||||||
|
* size than the one requested in the opus_encode*() call.
|
||||||
|
* It is then the user's responsibility
|
||||||
|
* to verify how much audio was encoded by checking the ToC byte of the encoded
|
||||||
|
* packet. The part of the audio that was not encoded needs to be resent to the
|
||||||
|
* encoder for the next call. Do not use this option unless you <b>really</b>
|
||||||
|
* know what you are doing.
|
||||||
|
* @see OPUS_GET_EXPERT_FRAME_DURATION
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 5 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_80_MS</dt><dd>Use 80 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_100_MS</dt><dd>Use 100 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_120_MS</dt><dd>Use 120 ms frames.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_EXPERT_FRAME_DURATION(x) OPUS_SET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured use of variable duration frames.
|
||||||
|
* @see OPUS_SET_EXPERT_FRAME_DURATION
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 5 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_80_MS</dt><dd>Use 80 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_100_MS</dt><dd>Use 100 ms frames.</dd>
|
||||||
|
* <dt>OPUS_FRAMESIZE_120_MS</dt><dd>Use 120 ms frames.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_EXPERT_FRAME_DURATION(x) OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** If set to 1, disables almost all use of prediction, making frames almost
|
||||||
|
* completely independent. This reduces quality.
|
||||||
|
* @see OPUS_GET_PREDICTION_DISABLED
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Enable prediction (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Disable prediction.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_PREDICTION_DISABLED(x) OPUS_SET_PREDICTION_DISABLED_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured prediction status.
|
||||||
|
* @see OPUS_SET_PREDICTION_DISABLED
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Prediction enabled (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Prediction disabled.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_PREDICTION_DISABLED(x) OPUS_GET_PREDICTION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_genericctls Generic CTLs
|
||||||
|
*
|
||||||
|
* These macros are used with the \c opus_decoder_ctl and
|
||||||
|
* \c opus_encoder_ctl calls to generate a particular
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* When called on an \c OpusDecoder they apply to that
|
||||||
|
* particular decoder instance. When called on an
|
||||||
|
* \c OpusEncoder they apply to the corresponding setting
|
||||||
|
* on that encoder instance, if present.
|
||||||
|
*
|
||||||
|
* Some usage examples:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* int ret;
|
||||||
|
* opus_int32 pitch;
|
||||||
|
* ret = opus_decoder_ctl(dec_ctx, OPUS_GET_PITCH(&pitch));
|
||||||
|
* if (ret == OPUS_OK) return ret;
|
||||||
|
*
|
||||||
|
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
||||||
|
* opus_decoder_ctl(dec_ctx, OPUS_RESET_STATE);
|
||||||
|
*
|
||||||
|
* opus_int32 enc_bw, dec_bw;
|
||||||
|
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&enc_bw));
|
||||||
|
* opus_decoder_ctl(dec_ctx, OPUS_GET_BANDWIDTH(&dec_bw));
|
||||||
|
* if (enc_bw != dec_bw) {
|
||||||
|
* printf("packet bandwidth mismatch!\n");
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl, opus_decoderctls, opus_encoderctls
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Resets the codec state to be equivalent to a freshly initialized state.
|
||||||
|
* This should be called when switching streams in order to prevent
|
||||||
|
* the back to back decoding from giving different results from
|
||||||
|
* one at a time decoding.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_RESET_STATE 4028
|
||||||
|
|
||||||
|
/** Gets the final state of the codec's entropy coder.
|
||||||
|
* This is used for testing purposes,
|
||||||
|
* The encoder and decoder state should be identical after coding a payload
|
||||||
|
* (assuming no data corruption or software bugs)
|
||||||
|
*
|
||||||
|
* @param[out] x <tt>opus_uint32 *</tt>: Entropy coder state
|
||||||
|
*
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_FINAL_RANGE(x) OPUS_GET_FINAL_RANGE_REQUEST, __opus_check_uint_ptr(x)
|
||||||
|
|
||||||
|
/** Gets the encoder's configured bandpass or the decoder's last bandpass.
|
||||||
|
* @see OPUS_SET_BANDWIDTH
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
||||||
|
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_BANDWIDTH(x) OPUS_GET_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Gets the sampling rate the encoder or decoder was initialized with.
|
||||||
|
* This simply returns the <code>Fs</code> value passed to opus_encoder_init()
|
||||||
|
* or opus_decoder_init().
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Sampling rate of encoder or decoder.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** If set to 1, disables the use of phase inversion for intensity stereo,
|
||||||
|
* improving the quality of mono downmixes, but slightly reducing normal
|
||||||
|
* stereo quality. Disabling phase inversion in the decoder does not comply
|
||||||
|
* with RFC 6716, although it does not cause any interoperability issue and
|
||||||
|
* is expected to become part of the Opus standard once RFC 6716 is updated
|
||||||
|
* by draft-ietf-codec-opus-update.
|
||||||
|
* @see OPUS_GET_PHASE_INVERSION_DISABLED
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Enable phase inversion (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Disable phase inversion.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_PHASE_INVERSION_DISABLED(x) OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the encoder's configured phase inversion status.
|
||||||
|
* @see OPUS_SET_PHASE_INVERSION_DISABLED
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>Stereo phase inversion enabled (default).</dd>
|
||||||
|
* <dt>1</dt><dd>Stereo phase inversion disabled.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_PHASE_INVERSION_DISABLED(x) OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
/** Gets the DTX state of the encoder.
|
||||||
|
* Returns whether the last encoded frame was either a comfort noise update
|
||||||
|
* during DTX or not encoded because of DTX.
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>The encoder is not in DTX.</dd>
|
||||||
|
* <dt>1</dt><dd>The encoder is in DTX.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_IN_DTX(x) OPUS_GET_IN_DTX_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_decoderctls Decoder related CTLs
|
||||||
|
* @see opus_genericctls, opus_encoderctls, opus_decoder
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Configures decoder gain adjustment.
|
||||||
|
* Scales the decoded output by a factor specified in Q8 dB units.
|
||||||
|
* This has a maximum range of -32768 to 32767 inclusive, and returns
|
||||||
|
* OPUS_BAD_ARG otherwise. The default is zero indicating no adjustment.
|
||||||
|
* This setting survives decoder reset.
|
||||||
|
*
|
||||||
|
* gain = pow(10, x/(20.0*256))
|
||||||
|
*
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: Amount to scale PCM signal by in Q8 dB units.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_SET_GAIN(x) OPUS_SET_GAIN_REQUEST, __opus_check_int(x)
|
||||||
|
/** Gets the decoder's configured gain adjustment. @see OPUS_SET_GAIN
|
||||||
|
*
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Amount to scale PCM signal by in Q8 dB units.
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_GAIN(x) OPUS_GET_GAIN_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Gets the duration (in samples) of the last packet successfully decoded or concealed.
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Number of samples (at current sampling rate).
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_LAST_PACKET_DURATION(x) OPUS_GET_LAST_PACKET_DURATION_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/** Gets the pitch of the last decoded frame, if available.
|
||||||
|
* This can be used for any post-processing algorithm requiring the use of pitch,
|
||||||
|
* e.g. time stretching/shortening. If the last frame was not voiced, or if the
|
||||||
|
* pitch was not coded in the frame, then zero is returned.
|
||||||
|
*
|
||||||
|
* This CTL is only implemented for decoder instances.
|
||||||
|
*
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: pitch period at 48 kHz (or 0 if not available)
|
||||||
|
*
|
||||||
|
* @hideinitializer */
|
||||||
|
#define OPUS_GET_PITCH(x) OPUS_GET_PITCH_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_libinfo Opus library information functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Converts an opus error code into a human readable string.
|
||||||
|
*
|
||||||
|
* @param[in] error <tt>int</tt>: Error number
|
||||||
|
* @returns Error string
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT const char *opus_strerror(int error);
|
||||||
|
|
||||||
|
/** Gets the libopus version string.
|
||||||
|
*
|
||||||
|
* Applications may look for the substring "-fixed" in the version string to
|
||||||
|
* determine whether they have a fixed-point or floating-point build at
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* @returns Version string
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT const char *opus_get_version_string(void);
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_DEFINES_H */
|
||||||
660
app/src/main/cpp/opus_multistream.h
Normal file
660
app/src/main/cpp/opus_multistream.h
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
/* Copyright (c) 2011 Xiph.Org Foundation
|
||||||
|
Written by Jean-Marc Valin */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file opus_multistream.h
|
||||||
|
* @brief Opus reference implementation multistream API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPUS_MULTISTREAM_H
|
||||||
|
#define OPUS_MULTISTREAM_H
|
||||||
|
|
||||||
|
#include "opus.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @cond OPUS_INTERNAL_DOC */
|
||||||
|
|
||||||
|
/** Macros to trigger compilation errors when the wrong types are provided to a
|
||||||
|
* CTL. */
|
||||||
|
/**@{*/
|
||||||
|
#define __opus_check_encstate_ptr(ptr) ((ptr) + ((ptr) - (OpusEncoder**)(ptr)))
|
||||||
|
#define __opus_check_decstate_ptr(ptr) ((ptr) + ((ptr) - (OpusDecoder**)(ptr)))
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** These are the actual encoder and decoder CTL ID numbers.
|
||||||
|
* They should not be used directly by applications.
|
||||||
|
* In general, SETs should be even and GETs should be odd.*/
|
||||||
|
/**@{*/
|
||||||
|
#define OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST 5120
|
||||||
|
#define OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST 5122
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/** @defgroup opus_multistream_ctls Multistream specific encoder and decoder CTLs
|
||||||
|
*
|
||||||
|
* These are convenience macros that are specific to the
|
||||||
|
* opus_multistream_encoder_ctl() and opus_multistream_decoder_ctl()
|
||||||
|
* interface.
|
||||||
|
* The CTLs from @ref opus_genericctls, @ref opus_encoderctls, and
|
||||||
|
* @ref opus_decoderctls may be applied to a multistream encoder or decoder as
|
||||||
|
* well.
|
||||||
|
* In addition, you may retrieve the encoder or decoder state for an specific
|
||||||
|
* stream via #OPUS_MULTISTREAM_GET_ENCODER_STATE or
|
||||||
|
* #OPUS_MULTISTREAM_GET_DECODER_STATE and apply CTLs to it individually.
|
||||||
|
*/
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the encoder state for an individual stream of a multistream encoder.
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: The index of the stream whose encoder you
|
||||||
|
* wish to retrieve.
|
||||||
|
* This must be non-negative and less than
|
||||||
|
* the <code>streams</code> parameter used
|
||||||
|
* to initialize the encoder.
|
||||||
|
* @param[out] y <tt>OpusEncoder**</tt>: Returns a pointer to the given
|
||||||
|
* encoder state.
|
||||||
|
* @retval OPUS_BAD_ARG The index of the requested stream was out of range.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_MULTISTREAM_GET_ENCODER_STATE(x,y) OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST, __opus_check_int(x), __opus_check_encstate_ptr(y)
|
||||||
|
|
||||||
|
/** Gets the decoder state for an individual stream of a multistream decoder.
|
||||||
|
* @param[in] x <tt>opus_int32</tt>: The index of the stream whose decoder you
|
||||||
|
* wish to retrieve.
|
||||||
|
* This must be non-negative and less than
|
||||||
|
* the <code>streams</code> parameter used
|
||||||
|
* to initialize the decoder.
|
||||||
|
* @param[out] y <tt>OpusDecoder**</tt>: Returns a pointer to the given
|
||||||
|
* decoder state.
|
||||||
|
* @retval OPUS_BAD_ARG The index of the requested stream was out of range.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_MULTISTREAM_GET_DECODER_STATE(x,y) OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST, __opus_check_int(x), __opus_check_decstate_ptr(y)
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @defgroup opus_multistream Opus Multistream API
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* The multistream API allows individual Opus streams to be combined into a
|
||||||
|
* single packet, enabling support for up to 255 channels. Unlike an
|
||||||
|
* elementary Opus stream, the encoder and decoder must negotiate the channel
|
||||||
|
* configuration before the decoder can successfully interpret the data in the
|
||||||
|
* packets produced by the encoder. Some basic information, such as packet
|
||||||
|
* duration, can be computed without any special negotiation.
|
||||||
|
*
|
||||||
|
* The format for multistream Opus packets is defined in
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc7845">RFC 7845</a>
|
||||||
|
* and is based on the self-delimited Opus framing described in Appendix B of
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc6716">RFC 6716</a>.
|
||||||
|
* Normal Opus packets are just a degenerate case of multistream Opus packets,
|
||||||
|
* and can be encoded or decoded with the multistream API by setting
|
||||||
|
* <code>streams</code> to <code>1</code> when initializing the encoder or
|
||||||
|
* decoder.
|
||||||
|
*
|
||||||
|
* Multistream Opus streams can contain up to 255 elementary Opus streams.
|
||||||
|
* These may be either "uncoupled" or "coupled", indicating that the decoder
|
||||||
|
* is configured to decode them to either 1 or 2 channels, respectively.
|
||||||
|
* The streams are ordered so that all coupled streams appear at the
|
||||||
|
* beginning.
|
||||||
|
*
|
||||||
|
* A <code>mapping</code> table defines which decoded channel <code>i</code>
|
||||||
|
* should be used for each input/output (I/O) channel <code>j</code>. This table is
|
||||||
|
* typically provided as an unsigned char array.
|
||||||
|
* Let <code>i = mapping[j]</code> be the index for I/O channel <code>j</code>.
|
||||||
|
* If <code>i < 2*coupled_streams</code>, then I/O channel <code>j</code> is
|
||||||
|
* encoded as the left channel of stream <code>(i/2)</code> if <code>i</code>
|
||||||
|
* is even, or as the right channel of stream <code>(i/2)</code> if
|
||||||
|
* <code>i</code> is odd. Otherwise, I/O channel <code>j</code> is encoded as
|
||||||
|
* mono in stream <code>(i - coupled_streams)</code>, unless it has the special
|
||||||
|
* value 255, in which case it is omitted from the encoding entirely (the
|
||||||
|
* decoder will reproduce it as silence). Each value <code>i</code> must either
|
||||||
|
* be the special value 255 or be less than <code>streams + coupled_streams</code>.
|
||||||
|
*
|
||||||
|
* The output channels specified by the encoder
|
||||||
|
* should use the
|
||||||
|
* <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9">Vorbis
|
||||||
|
* channel ordering</a>. A decoder may wish to apply an additional permutation
|
||||||
|
* to the mapping the encoder used to achieve a different output channel
|
||||||
|
* order (e.g. for outputing in WAV order).
|
||||||
|
*
|
||||||
|
* Each multistream packet contains an Opus packet for each stream, and all of
|
||||||
|
* the Opus packets in a single multistream packet must have the same
|
||||||
|
* duration. Therefore the duration of a multistream packet can be extracted
|
||||||
|
* from the TOC sequence of the first stream, which is located at the
|
||||||
|
* beginning of the packet, just like an elementary Opus stream:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* int nb_samples;
|
||||||
|
* int nb_frames;
|
||||||
|
* nb_frames = opus_packet_get_nb_frames(data, len);
|
||||||
|
* if (nb_frames < 1)
|
||||||
|
* return nb_frames;
|
||||||
|
* nb_samples = opus_packet_get_samples_per_frame(data, 48000) * nb_frames;
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The general encoding and decoding process proceeds exactly the same as in
|
||||||
|
* the normal @ref opus_encoder and @ref opus_decoder APIs.
|
||||||
|
* See their documentation for an overview of how to use the corresponding
|
||||||
|
* multistream functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Opus multistream encoder state.
|
||||||
|
* This contains the complete state of a multistream Opus encoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_multistream_encoder_create
|
||||||
|
* @see opus_multistream_encoder_init
|
||||||
|
*/
|
||||||
|
typedef struct OpusMSEncoder OpusMSEncoder;
|
||||||
|
|
||||||
|
/** Opus multistream decoder state.
|
||||||
|
* This contains the complete state of a multistream Opus decoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_multistream_decoder_create
|
||||||
|
* @see opus_multistream_decoder_init
|
||||||
|
*/
|
||||||
|
typedef struct OpusMSDecoder OpusMSDecoder;
|
||||||
|
|
||||||
|
/**\name Multistream encoder functions */
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the size of an OpusMSEncoder structure.
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
||||||
|
* to encode.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* encoded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @returns The size in bytes on success, or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on error.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_encoder_get_size(
|
||||||
|
int streams,
|
||||||
|
int coupled_streams
|
||||||
|
);
|
||||||
|
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_surround_encoder_get_size(
|
||||||
|
int channels,
|
||||||
|
int mapping_family
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Allocates and initializes a multistream encoder state.
|
||||||
|
* Call opus_multistream_encoder_destroy() to release
|
||||||
|
* this object when finished.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be greater than the number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
||||||
|
* input.
|
||||||
|
* This must be no more than the number of channels.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
||||||
|
* to encode.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* encoded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than the number of input channels.
|
||||||
|
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
||||||
|
* encoded channels to input channels, as described in
|
||||||
|
* @ref opus_multistream. As an extra constraint, the
|
||||||
|
* multistream encoder does not allow encoding coupled
|
||||||
|
* streams for which one channel is unused since this
|
||||||
|
* is never a good idea.
|
||||||
|
* @param application <tt>int</tt>: The target encoder application.
|
||||||
|
* This must be one of the following:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||||
|
* code (see @ref opus_errorcodes) on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_encoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
const unsigned char *mapping,
|
||||||
|
int application,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_surround_encoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int mapping_family,
|
||||||
|
int *streams,
|
||||||
|
int *coupled_streams,
|
||||||
|
unsigned char *mapping,
|
||||||
|
int application,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(4) OPUS_ARG_NONNULL(5) OPUS_ARG_NONNULL(6);
|
||||||
|
|
||||||
|
/** Initialize a previously allocated multistream encoder state.
|
||||||
|
* The memory pointed to by \a st must be at least the size returned by
|
||||||
|
* opus_multistream_encoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of
|
||||||
|
* malloc.
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @see opus_multistream_encoder_create
|
||||||
|
* @see opus_multistream_encoder_get_size
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to initialize.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be greater than the number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
||||||
|
* input.
|
||||||
|
* This must be no more than the number of channels.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
||||||
|
* to encode.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* encoded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than the number of input channels.
|
||||||
|
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
||||||
|
* encoded channels to input channels, as described in
|
||||||
|
* @ref opus_multistream. As an extra constraint, the
|
||||||
|
* multistream encoder does not allow encoding coupled
|
||||||
|
* streams for which one channel is unused since this
|
||||||
|
* is never a good idea.
|
||||||
|
* @param application <tt>int</tt>: The target encoder application.
|
||||||
|
* This must be one of the following:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_multistream_encoder_init(
|
||||||
|
OpusMSEncoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
const unsigned char *mapping,
|
||||||
|
int application
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
||||||
|
|
||||||
|
OPUS_EXPORT int opus_multistream_surround_encoder_init(
|
||||||
|
OpusMSEncoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int mapping_family,
|
||||||
|
int *streams,
|
||||||
|
int *coupled_streams,
|
||||||
|
unsigned char *mapping,
|
||||||
|
int application
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(5) OPUS_ARG_NONNULL(6) OPUS_ARG_NONNULL(7);
|
||||||
|
|
||||||
|
/** Encodes a multistream Opus frame.
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
||||||
|
* @param[in] pcm <tt>const opus_int16*</tt>: The input signal as interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||||
|
* signal.
|
||||||
|
* This must be an Opus frame size for the
|
||||||
|
* encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted values
|
||||||
|
* are 120, 240, 480, 960, 1920, and 2880.
|
||||||
|
* Passing in a duration of less than 10 ms
|
||||||
|
* (480 samples at 48 kHz) will prevent the
|
||||||
|
* encoder from using the LPC or hybrid modes.
|
||||||
|
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode(
|
||||||
|
OpusMSEncoder *st,
|
||||||
|
const opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Encodes a multistream Opus frame from floating point input.
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
||||||
|
* @param[in] pcm <tt>const float*</tt>: The input signal as interleaved
|
||||||
|
* samples with a normal range of
|
||||||
|
* +/-1.0.
|
||||||
|
* Samples with a range beyond +/-1.0
|
||||||
|
* are supported but will be clipped by
|
||||||
|
* decoders using the integer API and
|
||||||
|
* should only be used if it is known
|
||||||
|
* that the far end supports extended
|
||||||
|
* dynamic range.
|
||||||
|
* This must contain
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||||
|
* signal.
|
||||||
|
* This must be an Opus frame size for the
|
||||||
|
* encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted values
|
||||||
|
* are 120, 240, 480, 960, 1920, and 2880.
|
||||||
|
* Passing in a duration of less than 10 ms
|
||||||
|
* (480 samples at 48 kHz) will prevent the
|
||||||
|
* encoder from using the LPC or hybrid modes.
|
||||||
|
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode_float(
|
||||||
|
OpusMSEncoder *st,
|
||||||
|
const float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Frees an <code>OpusMSEncoder</code> allocated by
|
||||||
|
* opus_multistream_encoder_create().
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_multistream_encoder_destroy(OpusMSEncoder *st);
|
||||||
|
|
||||||
|
/** Perform a CTL function on a multistream Opus encoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated by a
|
||||||
|
* convenience macro.
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls,
|
||||||
|
* @ref opus_encoderctls, or @ref opus_multistream_ctls.
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_encoderctls
|
||||||
|
* @see opus_multistream_ctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**\name Multistream decoder functions */
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the size of an <code>OpusMSDecoder</code> structure.
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @returns The size in bytes on success, or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on error.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_decoder_get_size(
|
||||||
|
int streams,
|
||||||
|
int coupled_streams
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Allocates and initializes a multistream decoder state.
|
||||||
|
* Call opus_multistream_decoder_destroy() to release
|
||||||
|
* this object when finished.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels to output.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be different from the number of coded
|
||||||
|
* channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
||||||
|
* coded channels to output channels, as described in
|
||||||
|
* @ref opus_multistream.
|
||||||
|
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||||
|
* code (see @ref opus_errorcodes) on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSDecoder *opus_multistream_decoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
const unsigned char *mapping,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
/** Intialize a previously allocated decoder state object.
|
||||||
|
* The memory pointed to by \a st must be at least the size returned by
|
||||||
|
* opus_multistream_encoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of
|
||||||
|
* malloc.
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @see opus_multistream_decoder_create
|
||||||
|
* @see opus_multistream_deocder_get_size
|
||||||
|
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to initialize.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels to output.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be different from the number of coded
|
||||||
|
* channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
||||||
|
* coded channels to output channels, as described in
|
||||||
|
* @ref opus_multistream.
|
||||||
|
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_multistream_decoder_init(
|
||||||
|
OpusMSDecoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
const unsigned char *mapping
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
||||||
|
|
||||||
|
/** Decode a multistream Opus packet.
|
||||||
|
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
||||||
|
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||||
|
* Use a <code>NULL</code>
|
||||||
|
* pointer to indicate packet
|
||||||
|
* loss.
|
||||||
|
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||||
|
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain room for
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||||
|
* available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration
|
||||||
|
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||||
|
* of decoding some packets. In the case of PLC (data==NULL)
|
||||||
|
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||||
|
* the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the
|
||||||
|
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||||
|
* <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||||
|
* forward error correction data be decoded.
|
||||||
|
* If no such data is available, the frame is
|
||||||
|
* decoded as if it were lost.
|
||||||
|
* @returns Number of samples decoded on success or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode(
|
||||||
|
OpusMSDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Decode a multistream Opus packet with floating point output.
|
||||||
|
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
||||||
|
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||||
|
* Use a <code>NULL</code>
|
||||||
|
* pointer to indicate packet
|
||||||
|
* loss.
|
||||||
|
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||||
|
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain room for
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||||
|
* available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration
|
||||||
|
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||||
|
* of decoding some packets. In the case of PLC (data==NULL)
|
||||||
|
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||||
|
* the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the
|
||||||
|
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||||
|
* <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||||
|
* forward error correction data be decoded.
|
||||||
|
* If no such data is available, the frame is
|
||||||
|
* decoded as if it were lost.
|
||||||
|
* @returns Number of samples decoded on success or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode_float(
|
||||||
|
OpusMSDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
/** Perform a CTL function on a multistream Opus decoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated by a
|
||||||
|
* convenience macro.
|
||||||
|
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls,
|
||||||
|
* @ref opus_decoderctls, or @ref opus_multistream_ctls.
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_decoderctls
|
||||||
|
* @see opus_multistream_ctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
/** Frees an <code>OpusMSDecoder</code> allocated by
|
||||||
|
* opus_multistream_decoder_create().
|
||||||
|
* @param st <tt>OpusMSDecoder</tt>: Multistream decoder state to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_multistream_decoder_destroy(OpusMSDecoder *st);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_MULTISTREAM_H */
|
||||||
568
app/src/main/cpp/opus_projection.h
Normal file
568
app/src/main/cpp/opus_projection.h
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
/* Copyright (c) 2017 Google Inc.
|
||||||
|
Written by Andrew Allen */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file opus_projection.h
|
||||||
|
* @brief Opus projection reference API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPUS_PROJECTION_H
|
||||||
|
#define OPUS_PROJECTION_H
|
||||||
|
|
||||||
|
#include "opus_multistream.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @cond OPUS_INTERNAL_DOC */
|
||||||
|
|
||||||
|
/** These are the actual encoder and decoder CTL ID numbers.
|
||||||
|
* They should not be used directly by applications.c
|
||||||
|
* In general, SETs should be even and GETs should be odd.*/
|
||||||
|
/**@{*/
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST 6001
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST 6003
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST 6005
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/** @defgroup opus_projection_ctls Projection specific encoder and decoder CTLs
|
||||||
|
*
|
||||||
|
* These are convenience macros that are specific to the
|
||||||
|
* opus_projection_encoder_ctl() and opus_projection_decoder_ctl()
|
||||||
|
* interface.
|
||||||
|
* The CTLs from @ref opus_genericctls, @ref opus_encoderctls,
|
||||||
|
* @ref opus_decoderctls, and @ref opus_multistream_ctls may be applied to a
|
||||||
|
* projection encoder or decoder as well.
|
||||||
|
*/
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the gain (in dB. S7.8-format) of the demixing matrix from the encoder.
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns the gain (in dB. S7.8-format)
|
||||||
|
* of the demixing matrix.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN(x) OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
|
||||||
|
/** Gets the size in bytes of the demixing matrix from the encoder.
|
||||||
|
* @param[out] x <tt>opus_int32 *</tt>: Returns the size in bytes of the
|
||||||
|
* demixing matrix.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE(x) OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, __opus_check_int_ptr(x)
|
||||||
|
|
||||||
|
|
||||||
|
/** Copies the demixing matrix to the supplied pointer location.
|
||||||
|
* @param[out] x <tt>unsigned char *</tt>: Returns the demixing matrix to the
|
||||||
|
* supplied pointer location.
|
||||||
|
* @param y <tt>opus_int32</tt>: The size in bytes of the reserved memory at the
|
||||||
|
* pointer location.
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define OPUS_PROJECTION_GET_DEMIXING_MATRIX(x,y) OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, x, __opus_check_int(y)
|
||||||
|
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** Opus projection encoder state.
|
||||||
|
* This contains the complete state of a projection Opus encoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_projection_ambisonics_encoder_create
|
||||||
|
*/
|
||||||
|
typedef struct OpusProjectionEncoder OpusProjectionEncoder;
|
||||||
|
|
||||||
|
|
||||||
|
/** Opus projection decoder state.
|
||||||
|
* This contains the complete state of a projection Opus decoder.
|
||||||
|
* It is position independent and can be freely copied.
|
||||||
|
* @see opus_projection_decoder_create
|
||||||
|
* @see opus_projection_decoder_init
|
||||||
|
*/
|
||||||
|
typedef struct OpusProjectionDecoder OpusProjectionDecoder;
|
||||||
|
|
||||||
|
|
||||||
|
/**\name Projection encoder functions */
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the size of an OpusProjectionEncoder structure.
|
||||||
|
* @param channels <tt>int</tt>: The total number of input channels to encode.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param mapping_family <tt>int</tt>: The mapping family to use for selecting
|
||||||
|
* the appropriate projection.
|
||||||
|
* @returns The size in bytes on success, or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on error.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_projection_ambisonics_encoder_get_size(
|
||||||
|
int channels,
|
||||||
|
int mapping_family
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Allocates and initializes a projection encoder state.
|
||||||
|
* Call opus_projection_encoder_destroy() to release
|
||||||
|
* this object when finished.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be greater than the number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param mapping_family <tt>int</tt>: The mapping family to use for selecting
|
||||||
|
* the appropriate projection.
|
||||||
|
* @param[out] streams <tt>int *</tt>: The total number of streams that will
|
||||||
|
* be encoded from the input.
|
||||||
|
* @param[out] coupled_streams <tt>int *</tt>: Number of coupled (2 channel)
|
||||||
|
* streams that will be encoded from the input.
|
||||||
|
* @param application <tt>int</tt>: The target encoder application.
|
||||||
|
* This must be one of the following:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||||
|
* code (see @ref opus_errorcodes) on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int mapping_family,
|
||||||
|
int *streams,
|
||||||
|
int *coupled_streams,
|
||||||
|
int application,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(4) OPUS_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
|
||||||
|
/** Initialize a previously allocated projection encoder state.
|
||||||
|
* The memory pointed to by \a st must be at least the size returned by
|
||||||
|
* opus_projection_ambisonics_encoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of
|
||||||
|
* malloc.
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @see opus_projection_ambisonics_encoder_create
|
||||||
|
* @see opus_projection_ambisonics_encoder_get_size
|
||||||
|
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state to initialize.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be greater than the number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
||||||
|
* input.
|
||||||
|
* This must be no more than the number of channels.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
||||||
|
* to encode.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* encoded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than the number of input channels.
|
||||||
|
* @param application <tt>int</tt>: The target encoder application.
|
||||||
|
* This must be one of the following:
|
||||||
|
* <dl>
|
||||||
|
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
||||||
|
* <dd>Process signal for improved speech intelligibility.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
||||||
|
* <dd>Favor faithfulness to the original input.</dd>
|
||||||
|
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
||||||
|
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
||||||
|
* of operation.</dd>
|
||||||
|
* </dl>
|
||||||
|
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_projection_ambisonics_encoder_init(
|
||||||
|
OpusProjectionEncoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int mapping_family,
|
||||||
|
int *streams,
|
||||||
|
int *coupled_streams,
|
||||||
|
int application
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(5) OPUS_ARG_NONNULL(6);
|
||||||
|
|
||||||
|
|
||||||
|
/** Encodes a projection Opus frame.
|
||||||
|
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||||
|
* @param[in] pcm <tt>const opus_int16*</tt>: The input signal as interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||||
|
* signal.
|
||||||
|
* This must be an Opus frame size for the
|
||||||
|
* encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted values
|
||||||
|
* are 120, 240, 480, 960, 1920, and 2880.
|
||||||
|
* Passing in a duration of less than 10 ms
|
||||||
|
* (480 samples at 48 kHz) will prevent the
|
||||||
|
* encoder from using the LPC or hybrid modes.
|
||||||
|
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_encode(
|
||||||
|
OpusProjectionEncoder *st,
|
||||||
|
const opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
|
||||||
|
/** Encodes a projection Opus frame from floating point input.
|
||||||
|
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||||
|
* @param[in] pcm <tt>const float*</tt>: The input signal as interleaved
|
||||||
|
* samples with a normal range of
|
||||||
|
* +/-1.0.
|
||||||
|
* Samples with a range beyond +/-1.0
|
||||||
|
* are supported but will be clipped by
|
||||||
|
* decoders using the integer API and
|
||||||
|
* should only be used if it is known
|
||||||
|
* that the far end supports extended
|
||||||
|
* dynamic range.
|
||||||
|
* This must contain
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
||||||
|
* signal.
|
||||||
|
* This must be an Opus frame size for the
|
||||||
|
* encoder's sampling rate.
|
||||||
|
* For example, at 48 kHz the permitted values
|
||||||
|
* are 120, 240, 480, 960, 1920, and 2880.
|
||||||
|
* Passing in a duration of less than 10 ms
|
||||||
|
* (480 samples at 48 kHz) will prevent the
|
||||||
|
* encoder from using the LPC or hybrid modes.
|
||||||
|
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
||||||
|
* This must contain storage for at
|
||||||
|
* least \a max_data_bytes.
|
||||||
|
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
||||||
|
* memory for the output
|
||||||
|
* payload. This may be
|
||||||
|
* used to impose an upper limit on
|
||||||
|
* the instant bitrate, but should
|
||||||
|
* not be used as the only bitrate
|
||||||
|
* control. Use #OPUS_SET_BITRATE to
|
||||||
|
* control the bitrate.
|
||||||
|
* @returns The length of the encoded packet (in bytes) on success or a
|
||||||
|
* negative error code (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_encode_float(
|
||||||
|
OpusProjectionEncoder *st,
|
||||||
|
const float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
unsigned char *data,
|
||||||
|
opus_int32 max_data_bytes
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
|
||||||
|
/** Frees an <code>OpusProjectionEncoder</code> allocated by
|
||||||
|
* opus_projection_ambisonics_encoder_create().
|
||||||
|
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_projection_encoder_destroy(OpusProjectionEncoder *st);
|
||||||
|
|
||||||
|
|
||||||
|
/** Perform a CTL function on a projection Opus encoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated by a
|
||||||
|
* convenience macro.
|
||||||
|
* @param st <tt>OpusProjectionEncoder*</tt>: Projection encoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls,
|
||||||
|
* @ref opus_encoderctls, @ref opus_multistream_ctls, or
|
||||||
|
* @ref opus_projection_ctls
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_encoderctls
|
||||||
|
* @see opus_multistream_ctls
|
||||||
|
* @see opus_projection_ctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**\name Projection decoder functions */
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** Gets the size of an <code>OpusProjectionDecoder</code> structure.
|
||||||
|
* @param channels <tt>int</tt>: The total number of output channels.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @returns The size in bytes on success, or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on error.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_projection_decoder_get_size(
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/** Allocates and initializes a projection decoder state.
|
||||||
|
* Call opus_projection_decoder_destroy() to release
|
||||||
|
* this object when finished.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels to output.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be different from the number of coded
|
||||||
|
* channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @param[in] demixing_matrix <tt>const unsigned char[demixing_matrix_size]</tt>: Demixing matrix
|
||||||
|
* that mapping from coded channels to output channels,
|
||||||
|
* as described in @ref opus_projection and
|
||||||
|
* @ref opus_projection_ctls.
|
||||||
|
* @param demixing_matrix_size <tt>opus_int32</tt>: The size in bytes of the
|
||||||
|
* demixing matrix, as
|
||||||
|
* described in @ref
|
||||||
|
* opus_projection_ctls.
|
||||||
|
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
||||||
|
* code (see @ref opus_errorcodes) on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusProjectionDecoder *opus_projection_decoder_create(
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
unsigned char *demixing_matrix,
|
||||||
|
opus_int32 demixing_matrix_size,
|
||||||
|
int *error
|
||||||
|
) OPUS_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
|
||||||
|
/** Intialize a previously allocated projection decoder state object.
|
||||||
|
* The memory pointed to by \a st must be at least the size returned by
|
||||||
|
* opus_projection_decoder_get_size().
|
||||||
|
* This is intended for applications which use their own allocator instead of
|
||||||
|
* malloc.
|
||||||
|
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
||||||
|
* @see opus_projection_decoder_create
|
||||||
|
* @see opus_projection_deocder_get_size
|
||||||
|
* @param st <tt>OpusProjectionDecoder*</tt>: Projection encoder state to initialize.
|
||||||
|
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
||||||
|
* This must be one of 8000, 12000, 16000,
|
||||||
|
* 24000, or 48000.
|
||||||
|
* @param channels <tt>int</tt>: Number of channels to output.
|
||||||
|
* This must be at most 255.
|
||||||
|
* It may be different from the number of coded
|
||||||
|
* channels (<code>streams +
|
||||||
|
* coupled_streams</code>).
|
||||||
|
* @param streams <tt>int</tt>: The total number of streams coded in the
|
||||||
|
* input.
|
||||||
|
* This must be no more than 255.
|
||||||
|
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
||||||
|
* (2 channel) streams.
|
||||||
|
* This must be no larger than the total
|
||||||
|
* number of streams.
|
||||||
|
* Additionally, The total number of
|
||||||
|
* coded channels (<code>streams +
|
||||||
|
* coupled_streams</code>) must be no
|
||||||
|
* more than 255.
|
||||||
|
* @param[in] demixing_matrix <tt>const unsigned char[demixing_matrix_size]</tt>: Demixing matrix
|
||||||
|
* that mapping from coded channels to output channels,
|
||||||
|
* as described in @ref opus_projection and
|
||||||
|
* @ref opus_projection_ctls.
|
||||||
|
* @param demixing_matrix_size <tt>opus_int32</tt>: The size in bytes of the
|
||||||
|
* demixing matrix, as
|
||||||
|
* described in @ref
|
||||||
|
* opus_projection_ctls.
|
||||||
|
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_projection_decoder_init(
|
||||||
|
OpusProjectionDecoder *st,
|
||||||
|
opus_int32 Fs,
|
||||||
|
int channels,
|
||||||
|
int streams,
|
||||||
|
int coupled_streams,
|
||||||
|
unsigned char *demixing_matrix,
|
||||||
|
opus_int32 demixing_matrix_size
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
||||||
|
|
||||||
|
|
||||||
|
/** Decode a projection Opus packet.
|
||||||
|
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||||
|
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||||
|
* Use a <code>NULL</code>
|
||||||
|
* pointer to indicate packet
|
||||||
|
* loss.
|
||||||
|
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||||
|
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain room for
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||||
|
* available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration
|
||||||
|
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||||
|
* of decoding some packets. In the case of PLC (data==NULL)
|
||||||
|
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||||
|
* the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the
|
||||||
|
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||||
|
* <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||||
|
* forward error correction data be decoded.
|
||||||
|
* If no such data is available, the frame is
|
||||||
|
* decoded as if it were lost.
|
||||||
|
* @returns Number of samples decoded on success or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_decode(
|
||||||
|
OpusProjectionDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
opus_int16 *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
|
||||||
|
/** Decode a projection Opus packet with floating point output.
|
||||||
|
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||||
|
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
||||||
|
* Use a <code>NULL</code>
|
||||||
|
* pointer to indicate packet
|
||||||
|
* loss.
|
||||||
|
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
||||||
|
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
||||||
|
* samples.
|
||||||
|
* This must contain room for
|
||||||
|
* <code>frame_size*channels</code>
|
||||||
|
* samples.
|
||||||
|
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
||||||
|
* available space in \a pcm.
|
||||||
|
* If this is less than the maximum packet duration
|
||||||
|
* (120 ms; 5760 for 48kHz), this function will not be capable
|
||||||
|
* of decoding some packets. In the case of PLC (data==NULL)
|
||||||
|
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
||||||
|
* the duration of audio that is missing, otherwise the
|
||||||
|
* decoder will not be in the optimal state to decode the
|
||||||
|
* next incoming packet. For the PLC and FEC cases, frame_size
|
||||||
|
* <b>must</b> be a multiple of 2.5 ms.
|
||||||
|
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
||||||
|
* forward error correction data be decoded.
|
||||||
|
* If no such data is available, the frame is
|
||||||
|
* decoded as if it were lost.
|
||||||
|
* @returns Number of samples decoded on success or a negative error code
|
||||||
|
* (see @ref opus_errorcodes) on failure.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_projection_decode_float(
|
||||||
|
OpusProjectionDecoder *st,
|
||||||
|
const unsigned char *data,
|
||||||
|
opus_int32 len,
|
||||||
|
float *pcm,
|
||||||
|
int frame_size,
|
||||||
|
int decode_fec
|
||||||
|
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
||||||
|
|
||||||
|
|
||||||
|
/** Perform a CTL function on a projection Opus decoder.
|
||||||
|
*
|
||||||
|
* Generally the request and subsequent arguments are generated by a
|
||||||
|
* convenience macro.
|
||||||
|
* @param st <tt>OpusProjectionDecoder*</tt>: Projection decoder state.
|
||||||
|
* @param request This and all remaining parameters should be replaced by one
|
||||||
|
* of the convenience macros in @ref opus_genericctls,
|
||||||
|
* @ref opus_decoderctls, @ref opus_multistream_ctls, or
|
||||||
|
* @ref opus_projection_ctls.
|
||||||
|
* @see opus_genericctls
|
||||||
|
* @see opus_decoderctls
|
||||||
|
* @see opus_multistream_ctls
|
||||||
|
* @see opus_projection_ctls
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT int opus_projection_decoder_ctl(OpusProjectionDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
||||||
|
|
||||||
|
|
||||||
|
/** Frees an <code>OpusProjectionDecoder</code> allocated by
|
||||||
|
* opus_projection_decoder_create().
|
||||||
|
* @param st <tt>OpusProjectionDecoder</tt>: Projection decoder state to be freed.
|
||||||
|
*/
|
||||||
|
OPUS_EXPORT void opus_projection_decoder_destroy(OpusProjectionDecoder *st);
|
||||||
|
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_PROJECTION_H */
|
||||||
77
app/src/main/cpp/opus_recorder.cpp
Normal file
77
app/src/main/cpp/opus_recorder.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <string>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include "opus.h"
|
||||||
|
|
||||||
|
#define LOG_TAG "OpusJNI"
|
||||||
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||||
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Opus编码器句柄
|
||||||
|
static OpusEncoder *encoderHandle = nullptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusEncoder_nativeInitEncoder(JNIEnv *env, jobject thiz,
|
||||||
|
jint sample_rate, jint channels,
|
||||||
|
jint application) {
|
||||||
|
int error;
|
||||||
|
OpusEncoder *encoder = opus_encoder_create(sample_rate, channels, application, &error);
|
||||||
|
|
||||||
|
if (error != OPUS_OK || encoder == nullptr) {
|
||||||
|
LOGE("Failed to create encoder: %s", opus_strerror(error));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
opus_encoder_ctl(encoder, OPUS_SET_BITRATE(64000)); // 64 kbps
|
||||||
|
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(10)); // 0-10, 10是最高质量
|
||||||
|
|
||||||
|
LOGI("Opus encoder initialized: sample_rate=%d, channels=%d", sample_rate, channels);
|
||||||
|
return (jlong) (intptr_t) encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusEncoder_nativeEncodeBytes(JNIEnv *env, jobject thiz,
|
||||||
|
jlong encoder_handle,
|
||||||
|
jbyteArray input_buffer,
|
||||||
|
jint input_size,
|
||||||
|
jbyteArray output_buffer,
|
||||||
|
jint max_output_size) {
|
||||||
|
OpusEncoder *encoder = (OpusEncoder *) (intptr_t) encoder_handle;
|
||||||
|
if (encoder == nullptr) {
|
||||||
|
LOGE("Encoder handle is null");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyte *input = env->GetByteArrayElements(input_buffer, nullptr);
|
||||||
|
jbyte *output = env->GetByteArrayElements(output_buffer, nullptr);
|
||||||
|
|
||||||
|
opus_int16 *pcm = (opus_int16 *) input;
|
||||||
|
int frame_size = input_size / 2; // 16位samples的数量
|
||||||
|
|
||||||
|
int result = opus_encode(encoder, pcm, frame_size,
|
||||||
|
(unsigned char *) output, max_output_size);
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(input_buffer, input, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(output_buffer, output, 0);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
LOGE("Encoding failed: %s", opus_strerror(result));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_info_dourok_voicebot_OpusEncoder_nativeReleaseEncoder(JNIEnv *env, jobject thiz,
|
||||||
|
jlong encoder_handle) {
|
||||||
|
OpusEncoder *encoder = (OpusEncoder *) (intptr_t) encoder_handle;
|
||||||
|
if (encoder != nullptr) {
|
||||||
|
opus_encoder_destroy(encoder);
|
||||||
|
LOGI("Opus encoder released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
166
app/src/main/cpp/opus_types.h
Normal file
166
app/src/main/cpp/opus_types.h
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
|
||||||
|
/* Modified by Jean-Marc Valin */
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
/* opus_types.h based on ogg_types.h from libogg */
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file opus_types.h
|
||||||
|
@brief Opus reference implementation types
|
||||||
|
*/
|
||||||
|
#ifndef OPUS_TYPES_H
|
||||||
|
#define OPUS_TYPES_H
|
||||||
|
|
||||||
|
#define opus_int int /* used for counters etc; at least 16 bits */
|
||||||
|
#define opus_int64 long long
|
||||||
|
#define opus_int8 signed char
|
||||||
|
|
||||||
|
#define opus_uint unsigned int /* used for counters etc; at least 16 bits */
|
||||||
|
#define opus_uint64 unsigned long long
|
||||||
|
#define opus_uint8 unsigned char
|
||||||
|
|
||||||
|
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
|
||||||
|
#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
|
||||||
|
#include <stdint.h>
|
||||||
|
# undef opus_int64
|
||||||
|
# undef opus_int8
|
||||||
|
# undef opus_uint64
|
||||||
|
# undef opus_uint8
|
||||||
|
typedef int8_t opus_int8;
|
||||||
|
typedef uint8_t opus_uint8;
|
||||||
|
typedef int16_t opus_int16;
|
||||||
|
typedef uint16_t opus_uint16;
|
||||||
|
typedef int32_t opus_int32;
|
||||||
|
typedef uint32_t opus_uint32;
|
||||||
|
typedef int64_t opus_int64;
|
||||||
|
typedef uint64_t opus_uint64;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
# if defined(__CYGWIN__)
|
||||||
|
# include <_G_config.h>
|
||||||
|
typedef _G_int32_t opus_int32;
|
||||||
|
typedef _G_uint32_t opus_uint32;
|
||||||
|
typedef _G_int16 opus_int16;
|
||||||
|
typedef _G_uint16 opus_uint16;
|
||||||
|
# elif defined(__MINGW32__)
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
# elif defined(__MWERKS__)
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
# else
|
||||||
|
/* MSVC/Borland */
|
||||||
|
typedef __int32 opus_int32;
|
||||||
|
typedef unsigned __int32 opus_uint32;
|
||||||
|
typedef __int16 opus_int16;
|
||||||
|
typedef unsigned __int16 opus_uint16;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(__MACOS__)
|
||||||
|
|
||||||
|
# include <sys/types.h>
|
||||||
|
typedef SInt16 opus_int16;
|
||||||
|
typedef UInt16 opus_uint16;
|
||||||
|
typedef SInt32 opus_int32;
|
||||||
|
typedef UInt32 opus_uint32;
|
||||||
|
|
||||||
|
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
|
||||||
|
|
||||||
|
# include <sys/types.h>
|
||||||
|
typedef int16_t opus_int16;
|
||||||
|
typedef u_int16_t opus_uint16;
|
||||||
|
typedef int32_t opus_int32;
|
||||||
|
typedef u_int32_t opus_uint32;
|
||||||
|
|
||||||
|
#elif defined(__BEOS__)
|
||||||
|
|
||||||
|
/* Be */
|
||||||
|
# include <inttypes.h>
|
||||||
|
typedef int16 opus_int16;
|
||||||
|
typedef u_int16 opus_uint16;
|
||||||
|
typedef int32_t opus_int32;
|
||||||
|
typedef u_int32_t opus_uint32;
|
||||||
|
|
||||||
|
#elif defined (__EMX__)
|
||||||
|
|
||||||
|
/* OS/2 GCC */
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
|
||||||
|
#elif defined (DJGPP)
|
||||||
|
|
||||||
|
/* DJGPP */
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
|
||||||
|
#elif defined(R5900)
|
||||||
|
|
||||||
|
/* PS2 EE */
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned opus_uint32;
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
|
||||||
|
#elif defined(__SYMBIAN32__)
|
||||||
|
|
||||||
|
/* Symbian GCC */
|
||||||
|
typedef signed short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef signed int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
|
||||||
|
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
|
||||||
|
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef long opus_int32;
|
||||||
|
typedef unsigned long opus_uint32;
|
||||||
|
|
||||||
|
#elif defined(CONFIG_TI_C6X)
|
||||||
|
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Give up, take a reasonable guess */
|
||||||
|
typedef short opus_int16;
|
||||||
|
typedef unsigned short opus_uint16;
|
||||||
|
typedef int opus_int32;
|
||||||
|
typedef unsigned int opus_uint32;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OPUS_TYPES_H */
|
||||||
@ -25,11 +25,14 @@ class VadManager(
|
|||||||
private var lastActiveMs = 0L
|
private var lastActiveMs = 0L
|
||||||
private var speechStartMs = 0L
|
private var speechStartMs = 0L
|
||||||
|
|
||||||
// 配置参数
|
// 配置参数优化:适配预缓存逻辑,减少开头丢失概率
|
||||||
private val MIN_RMS = 0.003f
|
private val MIN_RMS = 0.002f // 降低能量阈值,避免弱语音开头被过滤
|
||||||
private val END_SILENCE_MS = 200L
|
private val END_SILENCE_MS = 100L // 保持不变
|
||||||
private val SPEECH_START_PROTECT_MS = 90L
|
private val SPEECH_START_PROTECT_MS = 30L // 缩短语音开始保护时间(从90→50ms),更快触发onSpeechStart
|
||||||
private val RESET_IDLE_MS = 3_000L
|
private val RESET_IDLE_MS = 3_000L // 保持不变
|
||||||
|
|
||||||
|
// 新增:标记是否已触发过语音开始回调,避免重复触发
|
||||||
|
private var speechStartTriggered = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
try {
|
try {
|
||||||
@ -38,14 +41,14 @@ class VadManager(
|
|||||||
VadModelConfig(
|
VadModelConfig(
|
||||||
sileroVadModelConfig = SileroVadModelConfig(
|
sileroVadModelConfig = SileroVadModelConfig(
|
||||||
model = "silero_vad.onnx",
|
model = "silero_vad.onnx",
|
||||||
threshold = 0.45F,
|
threshold = 0.40F, // 降低VAD检测阈值(从0.45→0.40),更灵敏检测开头语音
|
||||||
minSilenceDuration = 0.05F,
|
minSilenceDuration = 0.05F,
|
||||||
minSpeechDuration = 0.1F,
|
minSpeechDuration = 0.08F, // 缩短最小语音时长(从0.1→0.08s),更快识别短开头
|
||||||
windowSize = 512
|
windowSize = 512
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
LogUtils.i(TAG, "✅ VAD 初始化完成(线程安全版)")
|
LogUtils.i(TAG, "✅ VAD 初始化完成(线程安全+开头优化版)")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LogUtils.e(TAG, "❌ VAD 初始化失败", e)
|
LogUtils.e(TAG, "❌ VAD 初始化失败", e)
|
||||||
throw e
|
throw e
|
||||||
@ -63,7 +66,7 @@ class VadManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
// 快速能量检测
|
// 快速能量检测:优化采样步长,提高开头检测精度
|
||||||
val rms = fastRms(samples)
|
val rms = fastRms(samples)
|
||||||
if (rms < MIN_RMS) {
|
if (rms < MIN_RMS) {
|
||||||
handleSilence(now)
|
handleSilence(now)
|
||||||
@ -82,11 +85,13 @@ class VadManager(
|
|||||||
if (hasSpeech) {
|
if (hasSpeech) {
|
||||||
lastSpeechMs = now
|
lastSpeechMs = now
|
||||||
lastActiveMs = now
|
lastActiveMs = now
|
||||||
if (!isSpeaking) {
|
// 优化:避免重复触发onSpeechStart
|
||||||
|
if (!isSpeaking && !speechStartTriggered) {
|
||||||
isSpeaking = true
|
isSpeaking = true
|
||||||
|
speechStartTriggered = true // 标记已触发
|
||||||
speechStartMs = now
|
speechStartMs = now
|
||||||
onSpeechStart()
|
onSpeechStart() // 立即触发,不延迟
|
||||||
LogUtils.d(TAG, "🗣 语音开始 | RMS: $rms | 采样数: ${samples.size}")
|
LogUtils.d(TAG, "🗣 语音开始 | RMS: $rms | 采样数: ${samples.size} | 时间戳: $now")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleSilence(now)
|
handleSilence(now)
|
||||||
@ -115,6 +120,7 @@ class VadManager(
|
|||||||
&& now - lastSpeechMs > END_SILENCE_MS
|
&& now - lastSpeechMs > END_SILENCE_MS
|
||||||
) {
|
) {
|
||||||
isSpeaking = false
|
isSpeaking = false
|
||||||
|
speechStartTriggered = false // 重置触发标记
|
||||||
onSpeechEnd()
|
onSpeechEnd()
|
||||||
LogUtils.d(TAG, "🔇 语音结束 | 总时长: ${now - speechStartMs}ms")
|
LogUtils.d(TAG, "🔇 语音结束 | 总时长: ${now - speechStartMs}ms")
|
||||||
}
|
}
|
||||||
@ -123,6 +129,7 @@ class VadManager(
|
|||||||
if (!isSpeaking && now - lastActiveMs > RESET_IDLE_MS) {
|
if (!isSpeaking && now - lastActiveMs > RESET_IDLE_MS) {
|
||||||
try {
|
try {
|
||||||
vad.reset()
|
vad.reset()
|
||||||
|
speechStartTriggered = false // 重置触发标记
|
||||||
lastActiveMs = now
|
lastActiveMs = now
|
||||||
LogUtils.d(TAG, "🔄 VAD 空闲自动重置")
|
LogUtils.d(TAG, "🔄 VAD 空闲自动重置")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -131,11 +138,11 @@ class VadManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快速RMS计算逻辑保持不变
|
// 优化:调整RMS计算步长,提高开头弱语音的检测精度(从4→2)
|
||||||
private fun fastRms(samples: FloatArray): Float {
|
private fun fastRms(samples: FloatArray): Float {
|
||||||
var sum = 0f
|
var sum = 0f
|
||||||
var count = 0
|
var count = 0
|
||||||
val step = 4
|
val step = 2 // 缩小采样步长,更精准计算能量
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < samples.size) {
|
while (i < samples.size) {
|
||||||
val v = samples[i]
|
val v = samples[i]
|
||||||
@ -147,17 +154,18 @@ class VadManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 线程安全的重置方法
|
* 线程安全的重置方法:新增重置触发标记
|
||||||
*/
|
*/
|
||||||
suspend fun reset() {
|
suspend fun reset() {
|
||||||
vadMutex.withLock {
|
vadMutex.withLock {
|
||||||
isSpeaking = false
|
isSpeaking = false
|
||||||
|
speechStartTriggered = false // 关键:重置触发标记
|
||||||
lastSpeechMs = 0L
|
lastSpeechMs = 0L
|
||||||
lastActiveMs = 0L
|
lastActiveMs = 0L
|
||||||
speechStartMs = 0L
|
speechStartMs = 0L
|
||||||
try {
|
try {
|
||||||
vad.reset()
|
vad.reset()
|
||||||
LogUtils.d(TAG, "🔄 VAD 手动重置完成")
|
LogUtils.d(TAG, "🔄 VAD 手动重置完成(含触发标记)")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LogUtils.e(TAG, "❌ VAD 手动重置异常", e)
|
LogUtils.e(TAG, "❌ VAD 手动重置异常", e)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,14 +20,6 @@ import kotlinx.coroutines.asCoroutineDispatcher
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
// 1. 封装声纹验证状态(替代零散的Boolean标记)
|
|
||||||
private data class SpeakerVerifyState(
|
|
||||||
var job: Job? = null,
|
|
||||||
var finished: Boolean = false,
|
|
||||||
var passed: Boolean = true, // fail-open 默认放行
|
|
||||||
var failed: Boolean = false
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. 封装超时相关状态
|
// 2. 封装超时相关状态
|
||||||
private data class TimeoutState(
|
private data class TimeoutState(
|
||||||
var idleTimeoutMs: Long,
|
var idleTimeoutMs: Long,
|
||||||
@ -54,16 +46,11 @@ class VoiceController(
|
|||||||
private const val SAMPLE_RATE = 16000
|
private const val SAMPLE_RATE = 16000
|
||||||
private const val PRE_BUFFER_SIZE = SAMPLE_RATE * 2
|
private const val PRE_BUFFER_SIZE = SAMPLE_RATE * 2
|
||||||
private const val INVALID_RESET_DEBOUNCE_MS = 1500L
|
private const val INVALID_RESET_DEBOUNCE_MS = 1500L
|
||||||
private const val SPEAKER_THRESHOLD = 0.35f
|
private const val SPEAKER_THRESHOLD = 0.4f
|
||||||
private const val MIN_VERIFY_MS = 600L
|
private const val MIN_VERIFY_MS = 650L
|
||||||
private const val MAX_VERIFY_MS = 1200L
|
private const val MAX_VERIFY_MS = 1200L
|
||||||
private const val KWS_OBSERVE_MS = 500L
|
private const val KWS_OBSERVE_MS = 500L
|
||||||
private const val SPEECH_COOLDOWN_MS = 680L
|
private const val SPEECH_COOLDOWN_MS = 680L
|
||||||
|
|
||||||
// 声纹验证常量集中管理
|
|
||||||
private const val SPEAKER_VERIFY_NEED_SAMPLES_MS = 600L
|
|
||||||
private const val SPEAKER_VERIFY_MAX_WAIT_MS = 800L
|
|
||||||
private const val SPEAKER_VERIFY_CHECK_INTERVAL_MS = 20L
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= 核心优化:自定义IO线程池(减少调度开销) =================
|
// ================= 核心优化:自定义IO线程池(减少调度开销) =================
|
||||||
@ -85,9 +72,6 @@ class VoiceController(
|
|||||||
onStateChanged?.invoke(value)
|
onStateChanged?.invoke(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声纹验证状态封装(替代零散的Boolean)
|
|
||||||
private val speakerVerifyState = SpeakerVerifyState()
|
|
||||||
|
|
||||||
// 超时状态封装
|
// 超时状态封装
|
||||||
private val timeoutState = TimeoutState(
|
private val timeoutState = TimeoutState(
|
||||||
idleTimeoutMs = idleTimeoutSeconds * 1000L,
|
idleTimeoutMs = idleTimeoutSeconds * 1000L,
|
||||||
@ -331,7 +315,6 @@ class VoiceController(
|
|||||||
audioBufferSize += copySize
|
audioBufferSize += copySize
|
||||||
}
|
}
|
||||||
|
|
||||||
startSpeakerVerify()
|
|
||||||
state = VoiceState.RECORDING
|
state = VoiceState.RECORDING
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,84 +328,36 @@ class VoiceController(
|
|||||||
finishSentence()
|
finishSentence()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun verifySpeaker(audio: FloatArray): Boolean {
|
||||||
/* ================= 声纹验证(统一Job管理 + 原生数组优化) ================= */
|
val verifyStartNs = System.nanoTime()
|
||||||
private fun startSpeakerVerify() {
|
val totalSamples = audio.size
|
||||||
// 修复:彻底取消旧任务,防止回调泄漏
|
val tempTarget = min((SAMPLE_RATE * MAX_VERIFY_MS / 1000).toInt(), totalSamples)
|
||||||
speakerVerifyState.job?.cancel()
|
val targetSamples = max(tempTarget, (SAMPLE_RATE * MIN_VERIFY_MS / 1000).toInt())
|
||||||
speakerVerifyState.job = null
|
val midStartIndex = (totalSamples - targetSamples) / 2
|
||||||
// 修复:强制重置所有声纹状态,无残留
|
val finalAudio = if (midStartIndex >= 0 && midStartIndex + targetSamples <= totalSamples) {
|
||||||
resetSpeakerVerifyState()
|
audio.copyOfRange(midStartIndex, midStartIndex + targetSamples)
|
||||||
|
|
||||||
// 启动新的验证任务,绑定到统一作用域
|
|
||||||
speakerVerifyState.job = coroutineScope.launch(customIoDispatcher) {
|
|
||||||
val needSamples = (SAMPLE_RATE * SPEAKER_VERIFY_NEED_SAMPLES_MS / 1000).toInt()
|
|
||||||
var waited = 0L
|
|
||||||
|
|
||||||
// 等待足够的音频样本(使用audioBufferSize)
|
|
||||||
while (audioBufferSize < needSamples && waited < SPEAKER_VERIFY_MAX_WAIT_MS) {
|
|
||||||
delay(SPEAKER_VERIFY_CHECK_INTERVAL_MS)
|
|
||||||
waited += SPEAKER_VERIFY_CHECK_INTERVAL_MS
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioBufferSize < needSamples) {
|
|
||||||
speakerVerifyState.finished = true
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优化:直接从原生数组截取,无拷贝
|
|
||||||
val startIdx = maxOf(0, audioBufferSize - needSamples)
|
|
||||||
val input = audioBuffer.copyOfRange(startIdx, audioBufferSize)
|
|
||||||
val pass = verifySpeaker(input)
|
|
||||||
|
|
||||||
// 更新状态(线程安全)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
speakerVerifyState.passed = pass
|
|
||||||
speakerVerifyState.failed = !pass
|
|
||||||
speakerVerifyState.finished = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun verifySpeaker(audio: FloatArray): Boolean {
|
|
||||||
if (audio.isEmpty()) return false
|
|
||||||
|
|
||||||
val audioMs = audio.size * 1000L / SAMPLE_RATE
|
|
||||||
// 修复:严格模式下,短音频直接判定失败,不允许跳过
|
|
||||||
// if (audioMs < MIN_VERIFY_MS) {
|
|
||||||
// return if (ENABLE_STRICT_SPEAKER_VERIFY) {
|
|
||||||
// LogUtils.w(TAG, "🔴 严格模式:短音频 $audioMs ms,声纹验证失败")
|
|
||||||
// false
|
|
||||||
// } else {
|
|
||||||
// LogUtils.d(TAG, "🟡 非严格模式:短音频 $audioMs ms,跳过声纹")
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
val verifyStartMs = System.currentTimeMillis()
|
|
||||||
|
|
||||||
val maxSamples = (SAMPLE_RATE * MAX_VERIFY_MS / 1000).toInt()
|
|
||||||
val input = if (audio.size > maxSamples) {
|
|
||||||
audio.copyOfRange(audio.size - maxSamples, audio.size)
|
|
||||||
} else {
|
} else {
|
||||||
audio
|
val verifyCostMs = (System.nanoTime() - verifyStartNs) / 1_000_000
|
||||||
|
LogUtils.d(TAG, "🔍 声纹验证耗时: ${verifyCostMs}ms | 结果: 失败(有效语音过短)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return withContext(customIoDispatcher) {
|
return try {
|
||||||
var stream: OnlineStream? = null
|
var stream: OnlineStream? = null
|
||||||
try {
|
try {
|
||||||
stream = SpeakerRecognition.extractor.createStream()
|
stream = SpeakerRecognition.extractor.createStream()
|
||||||
stream.acceptWaveform(input, SAMPLE_RATE)
|
stream.acceptWaveform(finalAudio, SAMPLE_RATE) // 用中间段音频验证
|
||||||
stream.inputFinished()
|
stream.inputFinished()
|
||||||
|
|
||||||
if (!SpeakerRecognition.extractor.isReady(stream)) {
|
if (!SpeakerRecognition.extractor.isReady(stream)) {
|
||||||
LogUtils.w(TAG, "⚠️ stream not ready,验证失败")
|
val verifyCostMs = (System.nanoTime() - verifyStartNs) / 1_000_000
|
||||||
return@withContext false
|
LogUtils.d(TAG, "🔍 声纹验证耗时: ${verifyCostMs}ms | 结果: 失败(Stream未就绪)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val embedding = SpeakerRecognition.extractor.compute(stream)
|
val embedding = SpeakerRecognition.extractor.compute(stream)
|
||||||
|
|
||||||
val pass = speakerManagerLock.withLock {
|
val result = speakerManagerLock.withLock {
|
||||||
SpeakerRecognition.manager.verify(
|
SpeakerRecognition.manager.verify(
|
||||||
CURRENT_USER_ID,
|
CURRENT_USER_ID,
|
||||||
embedding,
|
embedding,
|
||||||
@ -430,76 +365,60 @@ class VoiceController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cost = System.currentTimeMillis() - verifyStartMs
|
// 计算总耗时(毫秒级,易读)
|
||||||
LogUtils.d(
|
val verifyCostMs = (System.nanoTime() - verifyStartNs) / 1_000_000
|
||||||
TAG,
|
LogUtils.d(TAG, "🔍 声纹验证耗时: ${verifyCostMs}ms | 结果: ${if (result) "通过" else "拒绝"}")
|
||||||
"📊 声纹 | pass=$pass | 音频=${audioMs}ms | 输入=${input.size} | 耗时=${cost}ms"
|
return result
|
||||||
)
|
|
||||||
|
|
||||||
// ================ 核心修复:移除状态判断,声纹失败强制触发终止流程 ================
|
|
||||||
if (!pass) {
|
|
||||||
withContext(Dispatchers.Main.immediate) {
|
|
||||||
// 标记失败状态
|
|
||||||
speakerVerifyState.failed = true
|
|
||||||
speakerVerifyState.finished = true
|
|
||||||
LogUtils.d(TAG, "🔴 声纹验证不通过,安全终止录音流程")
|
|
||||||
|
|
||||||
// 核心:主动结束录音,走标准finishSentence闭环,不暴力中断
|
|
||||||
finishSentence()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pass
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LogUtils.e(TAG, "❌ 声纹异常,验证失败", e)
|
|
||||||
false
|
|
||||||
} finally {
|
} finally {
|
||||||
stream?.release()
|
stream?.release()
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val verifyCostMs = (System.nanoTime() - verifyStartNs) / 1_000_000
|
||||||
|
LogUtils.d(TAG, "🔍 声纹验证耗时: ${verifyCostMs}ms | 结果: 失败(异常)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 结束录音(优化:原生数组读取) ================= */
|
|
||||||
private fun finishSentence() {
|
private fun finishSentence() {
|
||||||
// 取消旧任务
|
|
||||||
speakerVerifyState.job?.cancel()
|
|
||||||
speakerVerifyState.job = null
|
|
||||||
|
|
||||||
val now = cachedTimeMs
|
val now = cachedTimeMs
|
||||||
val duration = if (recordingStartMs != 0L) now - recordingStartMs else 0L
|
val duration = if (recordingStartMs != 0L) now - recordingStartMs else 0L
|
||||||
|
|
||||||
// 声纹失败优先拦截,直接重置,不上传音频
|
recordingStartMs = 0L
|
||||||
if (speakerVerifyState.failed) {
|
vadStarted = false
|
||||||
LogUtils.w(TAG, "❌ 声纹标记为失败,强制拒绝本次语音")
|
|
||||||
timeoutState.hasInvalidSpeech = true
|
|
||||||
// 重置到可交互状态,用户可以立即重新说话
|
|
||||||
resetToWaitSpeech()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ 原有正常逻辑保留 ============
|
// 无音频直接拒绝
|
||||||
val isSpeakerVerifyFailed = (ENABLE_STRICT_SPEAKER_VERIFY && speakerVerifyState.finished && !speakerVerifyState.passed)
|
|
||||||
if (isSpeakerVerifyFailed) {
|
|
||||||
LogUtils.w(TAG, "❌ 声纹验证未通过,拒绝本次语音")
|
|
||||||
timeoutState.hasInvalidSpeech = true
|
|
||||||
resetToWaitSpeech()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 无音频防护
|
|
||||||
if (audioBufferSize <= 0) {
|
if (audioBufferSize <= 0) {
|
||||||
LogUtils.w(TAG, "❌ 无有效音频数据,拒绝上传")
|
LogUtils.w(TAG, "❌ 无有效音频,丢弃")
|
||||||
resetToWaitSpeech()
|
resetToWaitSpeech()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正常上传逻辑
|
// 拷贝最终音频(此时 buffer 不再变化)
|
||||||
val audio = audioBuffer.copyOfRange(0, audioBufferSize)
|
val audio = audioBuffer.copyOfRange(0, audioBufferSize)
|
||||||
clearAudioBuffer()
|
clearAudioBuffer()
|
||||||
recordingStartMs = 0L
|
|
||||||
|
// ================= 声纹验证:只在这里做 =================
|
||||||
|
if (ENABLE_STRICT_SPEAKER_VERIFY) {
|
||||||
|
val pass = runCatching {
|
||||||
|
verifySpeaker(audio)
|
||||||
|
}.getOrElse {
|
||||||
|
LogUtils.e(TAG, "❌ 声纹异常,拒绝本次语音", it)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (!pass) {
|
||||||
|
LogUtils.w(TAG, "🔴 声纹验证失败,拒绝上传")
|
||||||
|
timeoutState.hasInvalidSpeech = true
|
||||||
|
resetToWaitSpeech()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= 通过后才上传 =================
|
||||||
|
timeoutState.hasInvalidSpeech = false
|
||||||
state = VoiceState.UPLOADING
|
state = VoiceState.UPLOADING
|
||||||
onFinalAudio(audio)
|
onFinalAudio(audio)
|
||||||
timeoutState.hasInvalidSpeech = false
|
|
||||||
LogUtils.i(TAG, "✅ 语音通过 | 时长: $duration ms")
|
LogUtils.i(TAG, "✅ 语音通过 | 时长: ${duration}ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 播放/上传回调(优化:精简状态判断) ================= */
|
/* ================= 播放/上传回调(优化:精简状态判断) ================= */
|
||||||
@ -560,21 +479,11 @@ class VoiceController(
|
|||||||
clearAudioBuffer()
|
clearAudioBuffer()
|
||||||
synchronized(preBufferLock) { preBuffer.clear() }
|
synchronized(preBufferLock) { preBuffer.clear() }
|
||||||
|
|
||||||
// 声纹失败场景:全量重置
|
|
||||||
if (speakerVerifyState.failed) {
|
|
||||||
LogUtils.d(TAG, "🛡 声纹失败,跳过防抖,强制全量重置")
|
|
||||||
resetSpeakerVerifyState()
|
|
||||||
timeoutState.lastInvalidResetMs = 0
|
|
||||||
timeoutState.waitSpeechFailStartMs = now
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 原有防抖逻辑
|
// 原有防抖逻辑
|
||||||
if (now - timeoutState.lastInvalidResetMs < INVALID_RESET_DEBOUNCE_MS) {
|
if (now - timeoutState.lastInvalidResetMs < INVALID_RESET_DEBOUNCE_MS) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSpeakerVerifyState()
|
|
||||||
timeoutState.lastInvalidResetMs = now
|
timeoutState.lastInvalidResetMs = now
|
||||||
timeoutState.waitSpeechFailStartMs = now
|
timeoutState.waitSpeechFailStartMs = now
|
||||||
}
|
}
|
||||||
@ -599,21 +508,9 @@ class VoiceController(
|
|||||||
timeoutState.hasInvalidSpeech = false
|
timeoutState.hasInvalidSpeech = false
|
||||||
timeoutState.currentType = TimeoutType.IDLE_TIMEOUT
|
timeoutState.currentType = TimeoutType.IDLE_TIMEOUT
|
||||||
|
|
||||||
// 重置声纹状态
|
|
||||||
resetSpeakerVerifyState()
|
|
||||||
|
|
||||||
state = VoiceState.WAIT_WAKEUP
|
state = VoiceState.WAIT_WAKEUP
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声纹状态重置(统一方法,修复:彻底清空所有标记)
|
|
||||||
private fun resetSpeakerVerifyState() {
|
|
||||||
speakerVerifyState.job?.cancel()
|
|
||||||
speakerVerifyState.job = null
|
|
||||||
speakerVerifyState.finished = false
|
|
||||||
speakerVerifyState.passed = true
|
|
||||||
speakerVerifyState.failed = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= 资源管理(优化:关闭自定义线程池) ================= */
|
/* ================= 资源管理(优化:关闭自定义线程池) ================= */
|
||||||
fun release() {
|
fun release() {
|
||||||
LogUtils.d(TAG, "🔌 释放资源")
|
LogUtils.d(TAG, "🔌 释放资源")
|
||||||
@ -634,7 +531,6 @@ class VoiceController(
|
|||||||
// 重置状态
|
// 重置状态
|
||||||
timeoutState.hasInvalidSpeech = false
|
timeoutState.hasInvalidSpeech = false
|
||||||
timeoutState.currentType = TimeoutType.IDLE_TIMEOUT
|
timeoutState.currentType = TimeoutType.IDLE_TIMEOUT
|
||||||
resetSpeakerVerifyState()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SpeakerRecognition.extractor.release()
|
SpeakerRecognition.extractor.release()
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class WakeupManager(assetManager: AssetManager, function: () -> Unit) {
|
|||||||
featConfig = featConfig,
|
featConfig = featConfig,
|
||||||
modelConfig = modelConfig,
|
modelConfig = modelConfig,
|
||||||
keywordsFile = keywordsFile,
|
keywordsFile = keywordsFile,
|
||||||
keywordsThreshold = 0.1f
|
keywordsThreshold = 0.06f
|
||||||
)
|
)
|
||||||
|
|
||||||
kws = KeywordSpotter(assetManager, config)
|
kws = KeywordSpotter(assetManager, config)
|
||||||
@ -49,11 +49,8 @@ class WakeupManager(assetManager: AssetManager, function: () -> Unit) {
|
|||||||
/** ⭐ 永远喂 KWS */
|
/** ⭐ 永远喂 KWS */
|
||||||
fun acceptAudio(samples: FloatArray) {
|
fun acceptAudio(samples: FloatArray) {
|
||||||
val s = stream ?: return
|
val s = stream ?: return
|
||||||
for (i in samples.indices) {
|
|
||||||
samples[i] *= 2.5f
|
|
||||||
}
|
|
||||||
s.acceptWaveform(samples, sampleRate)
|
s.acceptWaveform(samples, sampleRate)
|
||||||
|
normalize(samples)
|
||||||
while (kws.isReady(s)) {
|
while (kws.isReady(s)) {
|
||||||
kws.decode(s)
|
kws.decode(s)
|
||||||
val keyword = kws.getResult(s).keyword
|
val keyword = kws.getResult(s).keyword
|
||||||
@ -73,6 +70,18 @@ class WakeupManager(assetManager: AssetManager, function: () -> Unit) {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun normalize(samples: FloatArray, targetRms: Float = 0.05f) {
|
||||||
|
var sum = 0.0
|
||||||
|
for (s in samples) sum += s * s
|
||||||
|
val rms = kotlin.math.sqrt(sum / samples.size)
|
||||||
|
if (rms < 1e-6) return
|
||||||
|
val gain = targetRms / rms
|
||||||
|
for (i in samples.indices) {
|
||||||
|
samples[i] = (samples[i] * gain).coerceIn(-1.0, 1.0).toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
stream?.let { kws.reset(it) }
|
stream?.let { kws.reset(it) }
|
||||||
justWokeUp = false
|
justWokeUp = false
|
||||||
|
|||||||
76
app/src/main/java/com/zs/smarthuman/utils/OpusDecoder.kt
Normal file
76
app/src/main/java/com/zs/smarthuman/utils/OpusDecoder.kt
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package com.zs.smarthuman.utils;
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class OpusDecoder(
|
||||||
|
private val sampleRate: Int,
|
||||||
|
private val channels: Int,
|
||||||
|
frameSizeMs: Int
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "OpusDecoder"
|
||||||
|
|
||||||
|
init {
|
||||||
|
System.loadLibrary("app")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var nativeDecoderHandle: Long = 0
|
||||||
|
private val frameSize: Int = (sampleRate * frameSizeMs) / 1000
|
||||||
|
|
||||||
|
init {
|
||||||
|
nativeDecoderHandle = nativeInitDecoder(sampleRate, channels)
|
||||||
|
if (nativeDecoderHandle == 0L) {
|
||||||
|
throw IllegalStateException("Failed to initialize Opus decoder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用协程进行解码,运行在 IO 线程
|
||||||
|
suspend fun decode(opusData: ByteArray): ByteArray? = withContext(Dispatchers.IO) {
|
||||||
|
val maxPcmSize = frameSize * channels * 2 // 16-bit PCM
|
||||||
|
val pcmBuffer = ByteArray(maxPcmSize)
|
||||||
|
|
||||||
|
val decodedBytes = nativeDecodeBytes(
|
||||||
|
nativeDecoderHandle,
|
||||||
|
opusData,
|
||||||
|
opusData.size,
|
||||||
|
pcmBuffer,
|
||||||
|
maxPcmSize
|
||||||
|
)
|
||||||
|
|
||||||
|
if (decodedBytes > 0) {
|
||||||
|
if (decodedBytes < pcmBuffer.size) {
|
||||||
|
pcmBuffer.copyOf(decodedBytes)
|
||||||
|
} else {
|
||||||
|
pcmBuffer
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to decode frame")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release() {
|
||||||
|
if (nativeDecoderHandle != 0L) {
|
||||||
|
nativeReleaseDecoder(nativeDecoderHandle)
|
||||||
|
nativeDecoderHandle = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun finalize() {
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun nativeInitDecoder(sampleRate: Int, channels: Int): Long
|
||||||
|
private external fun nativeDecodeBytes(
|
||||||
|
decoderHandle: Long,
|
||||||
|
inputBuffer: ByteArray,
|
||||||
|
inputSize: Int,
|
||||||
|
outputBuffer: ByteArray,
|
||||||
|
maxOutputSize: Int
|
||||||
|
): Int
|
||||||
|
|
||||||
|
private external fun nativeReleaseDecoder(decoderHandle: Long)
|
||||||
|
}
|
||||||
75
app/src/main/java/com/zs/smarthuman/utils/OpusEncoder.kt
Normal file
75
app/src/main/java/com/zs/smarthuman/utils/OpusEncoder.kt
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package com.zs.smarthuman.utils;
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class OpusEncoder(
|
||||||
|
private val sampleRate: Int,
|
||||||
|
private val channels: Int,
|
||||||
|
frameSizeMs: Int
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "OpusEncoder"
|
||||||
|
|
||||||
|
init {
|
||||||
|
System.loadLibrary("app")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var nativeEncoderHandle: Long = 0
|
||||||
|
private val frameSize: Int = (sampleRate * frameSizeMs) / 1000
|
||||||
|
|
||||||
|
init {
|
||||||
|
nativeEncoderHandle = nativeInitEncoder(sampleRate, channels, 2048) // OPUS_APPLICATION_VOIP
|
||||||
|
if (nativeEncoderHandle == 0L) {
|
||||||
|
throw IllegalStateException("Failed to initialize Opus encoder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun encode(pcmData: ByteArray): ByteArray? = withContext(Dispatchers.IO) {
|
||||||
|
val frameBytes = frameSize * channels * 2 // 16-bit PCM
|
||||||
|
if (pcmData.size != frameBytes) {
|
||||||
|
Log.e(TAG, "Input buffer size must be $frameBytes bytes (got ${pcmData.size})")
|
||||||
|
return@withContext null
|
||||||
|
}
|
||||||
|
|
||||||
|
val outputBuffer = ByteArray(frameBytes) // 分配足够大的缓冲区
|
||||||
|
val encodedBytes = nativeEncodeBytes(
|
||||||
|
nativeEncoderHandle,
|
||||||
|
pcmData,
|
||||||
|
pcmData.size,
|
||||||
|
outputBuffer,
|
||||||
|
outputBuffer.size
|
||||||
|
)
|
||||||
|
|
||||||
|
if (encodedBytes > 0) {
|
||||||
|
outputBuffer.copyOf(encodedBytes)
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to encode frame")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release() {
|
||||||
|
if (nativeEncoderHandle != 0L) {
|
||||||
|
nativeReleaseEncoder(nativeEncoderHandle)
|
||||||
|
nativeEncoderHandle = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun finalize() {
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun nativeInitEncoder(sampleRate: Int, channels: Int, application: Int): Long
|
||||||
|
private external fun nativeEncodeBytes(
|
||||||
|
encoderHandle: Long,
|
||||||
|
inputBuffer: ByteArray,
|
||||||
|
inputSize: Int,
|
||||||
|
outputBuffer: ByteArray,
|
||||||
|
maxOutputSize: Int
|
||||||
|
): Int
|
||||||
|
|
||||||
|
private external fun nativeReleaseEncoder(encoderHandle: Long)
|
||||||
|
}
|
||||||
@ -0,0 +1,459 @@
|
|||||||
|
package com.zs.smarthuman.websocket
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
import com.blankj.utilcode.util.NetworkUtils
|
||||||
|
import com.zs.smarthuman.common.UserInfoManager
|
||||||
|
import com.zs.smarthuman.utils.AESUtils
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import okhttp3.*
|
||||||
|
import okio.ByteString
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
class WebsocketProtocol private constructor() {
|
||||||
|
companion object {
|
||||||
|
// 日志TAG
|
||||||
|
private const val TAG = "WS"
|
||||||
|
// WebSocket服务地址
|
||||||
|
private const val WS_URL = "ws://10.10.4.132:9001/aidialogue"
|
||||||
|
|
||||||
|
// 重连配置
|
||||||
|
private const val RECONNECT_DELAY = 3_000L // 重连基础延迟
|
||||||
|
private const val MAX_RECONNECT_COUNT = -1 // -1表示无限重连
|
||||||
|
private const val NETWORK_CHECK_INTERVAL = 5_000L // 网络检测间隔
|
||||||
|
|
||||||
|
// 线程安全的单例实现
|
||||||
|
val INSTANCE: WebsocketProtocol by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||||
|
WebsocketProtocol()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公开Flow
|
||||||
|
val incomingJsonFlow = MutableSharedFlow<JSONObject>(replay = 0)
|
||||||
|
val incomingByteArrayFlow = MutableSharedFlow<ByteArray>(replay = 0)
|
||||||
|
val networkErrorFlow = MutableSharedFlow<String>(replay = 0)
|
||||||
|
|
||||||
|
// 公开属性
|
||||||
|
var sessionId: String = "your_session_id"
|
||||||
|
|
||||||
|
// 线程安全状态标记(核心:所有标记都要闭环管理)
|
||||||
|
private val isOpen = AtomicBoolean(false) // 连接是否已打开
|
||||||
|
private val isAuthSuccess = AtomicBoolean(false) // 认证是否成功
|
||||||
|
private val reconnectCount = AtomicInteger(0) // 重连次数
|
||||||
|
private val isReconnecting = AtomicBoolean(false) // 是否正在重连(核心标记)
|
||||||
|
private val isOpening = AtomicBoolean(false) // 是否正在执行打开操作
|
||||||
|
private val connectLock = ReentrantLock() // 连接操作锁
|
||||||
|
|
||||||
|
// 协程作用域
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob() + CoroutineName("WS-Scope"))
|
||||||
|
|
||||||
|
// WebSocket实例(volatile保证可见性)
|
||||||
|
@Volatile
|
||||||
|
private var websocket: WebSocket? = null
|
||||||
|
|
||||||
|
// OkHttp客户端
|
||||||
|
private val client by lazy {
|
||||||
|
OkHttpClient.Builder()
|
||||||
|
.connectTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.pingInterval(3, TimeUnit.SECONDS)
|
||||||
|
.retryOnConnectionFailure(false)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送音频数据
|
||||||
|
*/
|
||||||
|
fun sendAudio(data: ByteArray) {
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
LogUtils.i(TAG, "Sending audio: ${data.size} bytes")
|
||||||
|
if (isOpen.get() && isAuthSuccess.get() && websocket != null) {
|
||||||
|
websocket?.send(ByteString.of(*data))
|
||||||
|
} else {
|
||||||
|
val errorMsg = "WebSocket not ready (open:${isOpen.get()}, auth:${isAuthSuccess.get()})"
|
||||||
|
LogUtils.eTag(TAG, errorMsg)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
LogUtils.eTag(TAG, "Send audio failed: ${e.message}", e)
|
||||||
|
networkErrorFlow.emit("Send audio error: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送文本数据
|
||||||
|
*/
|
||||||
|
fun sendText(text: String) {
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
LogUtils.i(TAG, "Sending text: $text")
|
||||||
|
if (isOpen.get() && isAuthSuccess.get() && websocket != null) {
|
||||||
|
websocket?.send(text)
|
||||||
|
} else {
|
||||||
|
val errorMsg = "WebSocket not ready (open:${isOpen.get()}, auth:${isAuthSuccess.get()})"
|
||||||
|
LogUtils.eTag(TAG, errorMsg)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
LogUtils.eTag(TAG, "Send text failed: ${e.message}", e)
|
||||||
|
networkErrorFlow.emit("Send text error: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断通道是否就绪
|
||||||
|
*/
|
||||||
|
fun isAudioChannelOpened(): Boolean {
|
||||||
|
return websocket != null && isOpen.get() && isAuthSuccess.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭通道
|
||||||
|
*/
|
||||||
|
fun closeAudioChannel() {
|
||||||
|
scope.launch {
|
||||||
|
connectLock.withLock {
|
||||||
|
LogUtils.iTag(TAG, "Close audio channel")
|
||||||
|
isOpen.set(false)
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
reconnectCount.set(0)
|
||||||
|
isReconnecting.set(false) // 关闭时重置重连标记
|
||||||
|
isOpening.set(false) // 关闭时重置打开标记
|
||||||
|
|
||||||
|
val ws = websocket
|
||||||
|
websocket = null
|
||||||
|
ws?.close(1000, "Normal closure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开通道(核心修复:标记闭环)
|
||||||
|
*/
|
||||||
|
suspend fun openAudioChannel(): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
// 1. 防并发:只有未打开时才执行
|
||||||
|
if (!isOpening.compareAndSet(false, true)) {
|
||||||
|
LogUtils.wTag(TAG, "openAudioChannel is running, skip")
|
||||||
|
return@withContext false
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = false
|
||||||
|
connectLock.withLock {
|
||||||
|
try {
|
||||||
|
// 2. 如果已有连接,直接返回成功并重置标记
|
||||||
|
if (websocket != null && isOpen.get()) {
|
||||||
|
LogUtils.iTag(TAG, "WebSocket already connected, skip open")
|
||||||
|
result = true
|
||||||
|
isReconnecting.set(false) // 关键:重置重连标记
|
||||||
|
return@withContext result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查网络
|
||||||
|
if (!NetworkUtils.isConnected()) {
|
||||||
|
val errorMsg = "Network disconnected, can't connect WebSocket"
|
||||||
|
LogUtils.eTag(TAG, errorMsg)
|
||||||
|
scope.launch {
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
}
|
||||||
|
result = false
|
||||||
|
return@withContext result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 创建新连接(核心:先创建再赋值)
|
||||||
|
val request = Request.Builder().url(WS_URL).build()
|
||||||
|
val newWs = client.newWebSocket(request, createWebSocketListener())
|
||||||
|
websocket = newWs
|
||||||
|
LogUtils.iTag(TAG, "WebSocket connecting to: $WS_URL")
|
||||||
|
result = true
|
||||||
|
isReconnecting.set(false) // 关键:发起连接后重置重连标记
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val errorMsg = "Create WebSocket failed: ${e.message}"
|
||||||
|
LogUtils.eTag(TAG, errorMsg, e)
|
||||||
|
scope.launch {
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
}
|
||||||
|
websocket = null
|
||||||
|
result = false
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
isOpening.set(false) // 无论成败,最终重置打开标记
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@withContext result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建WebSocket监听器
|
||||||
|
*/
|
||||||
|
private fun createWebSocketListener(): WebSocketListener {
|
||||||
|
return object : WebSocketListener() {
|
||||||
|
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||||
|
connectLock.withLock {
|
||||||
|
if (websocket === webSocket) {
|
||||||
|
LogUtils.iTag(TAG, "✅ WebSocket connected, start auth")
|
||||||
|
isOpen.set(true)
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
reconnectCount.set(0) // 连接成功重置重连次数
|
||||||
|
isReconnecting.set(false) // 关键:连接成功重置重连标记
|
||||||
|
sendAuthMessage(webSocket)
|
||||||
|
} else {
|
||||||
|
LogUtils.wTag(TAG, "Ignore onOpen for old WebSocket")
|
||||||
|
webSocket.close(1001, "New connection exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
|
if (websocket !== webSocket) {
|
||||||
|
LogUtils.wTag(TAG, "Ignore text message for old WebSocket")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.iTag(TAG, "📩 Receive text: $text")
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
val json = JSONObject(text)
|
||||||
|
when (json.optInt("msgContentType")) {
|
||||||
|
WebsocketType.AUTH.code -> parseAuthResponse(json)
|
||||||
|
WebsocketType.HEARTBEAT.code -> sendHeartbeat()
|
||||||
|
else -> incomingJsonFlow.emit(json)
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
val errorMsg = "Parse text message error: ${e.message}"
|
||||||
|
LogUtils.eTag(TAG, errorMsg, e)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
|
||||||
|
if (websocket !== webSocket) {
|
||||||
|
LogUtils.wTag(TAG, "Ignore binary message for old WebSocket")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val size = bytes.size
|
||||||
|
LogUtils.iTag(TAG, "📩 Receive binary: $size bytes")
|
||||||
|
if (isAuthSuccess.get()) {
|
||||||
|
scope.launch { incomingByteArrayFlow.emit(bytes.toByteArray()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
|
connectLock.withLock {
|
||||||
|
if (websocket === webSocket) {
|
||||||
|
LogUtils.wTag(TAG, "🔌 WebSocket closed: $code, reason: $reason")
|
||||||
|
isOpen.set(false)
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
websocket = null
|
||||||
|
isReconnecting.set(false) // 关闭时重置重连标记
|
||||||
|
} else {
|
||||||
|
LogUtils.wTag(TAG, "Ignore onClosed for old WebSocket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非主动关闭才重连
|
||||||
|
val isNormalClose = code == 1000 && reason == "Normal closure"
|
||||||
|
if (!isNormalClose) {
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||||
|
connectLock.withLock {
|
||||||
|
if (websocket === webSocket) {
|
||||||
|
LogUtils.eTag(TAG, "❌ WebSocket failure: ${t.message}", t)
|
||||||
|
isOpen.set(false)
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
websocket = null
|
||||||
|
} else {
|
||||||
|
LogUtils.wTag(TAG, "Ignore onFailure for old WebSocket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
networkErrorFlow.emit(t.message ?: "Unknown WebSocket error")
|
||||||
|
}
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送认证消息
|
||||||
|
*/
|
||||||
|
private fun sendAuthMessage(webSocket: WebSocket) {
|
||||||
|
if (websocket !== webSocket) {
|
||||||
|
LogUtils.wTag(TAG, "Ignore auth for old WebSocket")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
val authData = JSONObject().apply {
|
||||||
|
put("unionId", AESUtils.encrypt(UserInfoManager.sn))
|
||||||
|
}
|
||||||
|
val authMsg = buildWsMessage(WebsocketType.AUTH, authData)
|
||||||
|
LogUtils.iTag(TAG, "🔑 Send auth: $authMsg")
|
||||||
|
webSocket.send(authMsg.toString())
|
||||||
|
}.onFailure { e ->
|
||||||
|
val errorMsg = "Send auth failed: ${e.message}"
|
||||||
|
LogUtils.eTag(TAG, errorMsg, e)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送心跳
|
||||||
|
*/
|
||||||
|
private fun sendHeartbeat() {
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
val heartbeatMsg = buildWsMessage(WebsocketType.HEARTBEAT, "pong")
|
||||||
|
LogUtils.iTag(TAG, "💓 Send heartbeat: $heartbeatMsg")
|
||||||
|
sendText(heartbeatMsg.toString())
|
||||||
|
}.onFailure { e ->
|
||||||
|
val errorMsg = "Send heartbeat failed: ${e.message}"
|
||||||
|
LogUtils.eTag(TAG, errorMsg, e)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建通用消息
|
||||||
|
*/
|
||||||
|
private fun buildWsMessage(type: WebsocketType, data: Any): JSONObject {
|
||||||
|
return JSONObject().apply {
|
||||||
|
put("msgContentType", type.code)
|
||||||
|
put("timeStamp", System.currentTimeMillis())
|
||||||
|
put("msgId", UUID.randomUUID().toString())
|
||||||
|
put("data", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析认证响应
|
||||||
|
*/
|
||||||
|
private fun parseAuthResponse(root: JSONObject) {
|
||||||
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
|
val dataObj = root.optJSONObject("data")
|
||||||
|
?: throw IllegalArgumentException("Auth response missing 'data' field")
|
||||||
|
|
||||||
|
val code = dataObj.optInt("code")
|
||||||
|
val msg = dataObj.optString("msg")
|
||||||
|
val authResult = dataObj.optString("data")
|
||||||
|
|
||||||
|
if (code == 200 && authResult == "WS_AUTH_SUCCESS") {
|
||||||
|
isAuthSuccess.set(true)
|
||||||
|
LogUtils.iTag(TAG, "✅ Auth success")
|
||||||
|
} else {
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
val errorMsg = "Auth failed: $msg (code:$code)"
|
||||||
|
LogUtils.eTag(TAG, errorMsg)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
isAuthSuccess.set(false)
|
||||||
|
val errorMsg = "Parse auth response error: ${e.message}"
|
||||||
|
LogUtils.eTag(TAG, errorMsg, e)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
launchReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心修复:重连逻辑(标记闭环管理)
|
||||||
|
*/
|
||||||
|
private fun launchReconnect() {
|
||||||
|
// 1. 防止重复进入重连
|
||||||
|
if (!isReconnecting.compareAndSet(false, true)) {
|
||||||
|
LogUtils.wTag(TAG, "Already reconnecting, skip launchReconnect")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 协程执行重连
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
// 循环重连直到成功/达到最大次数
|
||||||
|
while (true) {
|
||||||
|
// 检查最大重连次数
|
||||||
|
if (MAX_RECONNECT_COUNT != -1 && reconnectCount.get() >= MAX_RECONNECT_COUNT) {
|
||||||
|
val errorMsg = "Reach max reconnect count: $MAX_RECONNECT_COUNT"
|
||||||
|
LogUtils.eTag(TAG, errorMsg)
|
||||||
|
networkErrorFlow.emit(errorMsg)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentCount = reconnectCount.incrementAndGet()
|
||||||
|
LogUtils.wTag(TAG, "🔄 Reconnect attempt: $currentCount, delay: ${RECONNECT_DELAY}ms")
|
||||||
|
|
||||||
|
// 等待重连延迟
|
||||||
|
delay(RECONNECT_DELAY)
|
||||||
|
|
||||||
|
// 检查网络
|
||||||
|
if (!NetworkUtils.isConnected()) {
|
||||||
|
LogUtils.wTag(TAG, "🔌 Network disconnected, wait ${NETWORK_CHECK_INTERVAL}ms")
|
||||||
|
delay(NETWORK_CHECK_INTERVAL)
|
||||||
|
continue // 网络未恢复,继续循环
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络恢复,尝试连接
|
||||||
|
LogUtils.iTag(TAG, "🔌 Network available, try to connect")
|
||||||
|
val connectSuccess = openAudioChannel()
|
||||||
|
|
||||||
|
// 连接成功则退出循环
|
||||||
|
if (connectSuccess) {
|
||||||
|
LogUtils.iTag(TAG, "✅ Reconnect success")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接失败,继续循环
|
||||||
|
LogUtils.wTag(TAG, "❌ Reconnect attempt $currentCount failed")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LogUtils.eTag(TAG, "Reconnect loop error: ${e.message}", e)
|
||||||
|
networkErrorFlow.emit("Reconnect error: ${e.message}")
|
||||||
|
} finally {
|
||||||
|
// 最终必须重置重连标记(核心修复)
|
||||||
|
isReconnecting.set(false)
|
||||||
|
LogUtils.iTag(TAG, "Reconnect loop finished, reset isReconnecting to false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放资源
|
||||||
|
*/
|
||||||
|
fun dispose() {
|
||||||
|
LogUtils.iTag(TAG, "🗑️ Dispose WebSocketProtocol")
|
||||||
|
scope.cancel("Dispose called")
|
||||||
|
|
||||||
|
connectLock.withLock {
|
||||||
|
closeAudioChannel()
|
||||||
|
websocket = null
|
||||||
|
isReconnecting.set(false)
|
||||||
|
isOpening.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.dispatcher.executorService.shutdown()
|
||||||
|
client.connectionPool.evictAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.zs.smarthuman.websocket
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description:
|
||||||
|
* @author: lrs
|
||||||
|
* @date: 2026/2/10 18:59
|
||||||
|
*/
|
||||||
|
enum class WebsocketType(val code: Int) {
|
||||||
|
AUTH(1001),
|
||||||
|
HEARTBEAT(1002);
|
||||||
|
}
|
||||||
BIN
app/src/main/jniLibs/arm64-v8a/libopus.so
Normal file
BIN
app/src/main/jniLibs/arm64-v8a/libopus.so
Normal file
Binary file not shown.
BIN
app/src/main/jniLibs/armeabi-v7a/libopus.so
Normal file
BIN
app/src/main/jniLibs/armeabi-v7a/libopus.so
Normal file
Binary file not shown.
@ -35,7 +35,7 @@ immersionbar = "3.2.2"
|
|||||||
immersionbarKtx = "3.2.2"
|
immersionbarKtx = "3.2.2"
|
||||||
immersionbarComponents = "3.2.2"
|
immersionbarComponents = "3.2.2"
|
||||||
lifecycleRuntimeAndroid = "2.9.1"
|
lifecycleRuntimeAndroid = "2.9.1"
|
||||||
|
opus = "1.3.1"
|
||||||
[libraries]
|
[libraries]
|
||||||
android-spinkit = { module = "com.github.ybq:Android-SpinKit", version.ref = "androidSpinkit" }
|
android-spinkit = { module = "com.github.ybq:Android-SpinKit", version.ref = "androidSpinkit" }
|
||||||
androidautosize = { module = "com.github.JessYanCoding:AndroidAutoSize", version.ref = "androidautosize" }
|
androidautosize = { module = "com.github.JessYanCoding:AndroidAutoSize", version.ref = "androidautosize" }
|
||||||
@ -73,7 +73,7 @@ immersionbar-components = { module = "com.geyifeng.immersionbar:immersionbar-com
|
|||||||
immersionbar-ktx = { module = "com.geyifeng.immersionbar:immersionbar-ktx", version.ref = "immersionbarKtx" }
|
immersionbar-ktx = { module = "com.geyifeng.immersionbar:immersionbar-ktx", version.ref = "immersionbarKtx" }
|
||||||
immersionbar = { module = "com.geyifeng.immersionbar:immersionbar", version.ref = "immersionbar" }
|
immersionbar = { module = "com.geyifeng.immersionbar:immersionbar", version.ref = "immersionbar" }
|
||||||
androidx-lifecycle-runtime-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-android", version.ref = "lifecycleRuntimeAndroid" }
|
androidx-lifecycle-runtime-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-android", version.ref = "lifecycleRuntimeAndroid" }
|
||||||
|
opus-v131 = { module = "com.fpliu.ndk.pkg.prefab.android.21:opus", version.ref = "opus" }
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
|||||||
@ -16,6 +16,10 @@ pluginManagement {
|
|||||||
maven { setUrl("https://maven.aliyun.com/repository/public") }
|
maven { setUrl("https://maven.aliyun.com/repository/public") }
|
||||||
maven { url "https://jitpack.io" }
|
maven { url "https://jitpack.io" }
|
||||||
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
|
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
|
||||||
|
|
||||||
|
maven {
|
||||||
|
url = uri("https://raw.githubusercontent.com/leleliu008/ndk-pkg-prefab-aar-maven-repo/master")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
@ -39,6 +43,9 @@ dependencyResolutionManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url "https://jitpack.io" }
|
maven { url "https://jitpack.io" }
|
||||||
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
|
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
|
||||||
|
maven {
|
||||||
|
url = uri("https://raw.githubusercontent.com/leleliu008/ndk-pkg-prefab-aar-maven-repo/master")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user