/// 状态码回调
public var codeCallback:((Int)->Void)?
/// 录音文件回调
- public var filePathCallBack:((String?)->Void)?
+ public var filePathCallBack:((String)->Void)?
/// opus回调
public var opusCallBack:((Data)->Void)?
var asrOtherText = ""
init() {
- setupRecord()
+// setupRecord()
}
func setupRecord(){
} recognizeText: { [weak self] model in
guard let self = self else {return}
guard let model = model else {return}
- DCLogger.debug(model.channel,"asr =",model.definiteIndex,model.textResult)
+// DCLogger.debug(model.channel,"asr =",model.definiteIndex,model.textResult)
if let text = model.textResult,!text.isEmpty{
if model.channel == 1 {
self?.codeCallback?(code.rawValue)
} finished: {[weak self] filePath in
DCLogger.debug("文件录音保存路径 \(filePath ?? "")")
- self?.filePathCallBack?(filePath)
+ self?.filePathCallBack?(filePath ?? "")
}
//通话录音声道数。1:单声道 ;2:⽴体声;默认单声道
extension DCLogger {
public static func debug(_ logs: Any..., line: Int = #line, separator: String = " ", terminator: String = "\n") {
- objc_sync_enter(DCLogger.shared)
+
if DCLogger.isDebug {
+ objc_sync_enter(DCLogger.shared)
print(DCLogger.shared.dateString, terminator: " [DeepChordsSdk-\(String(format: "%03d", line))]: ")
print(logs, separator: separator, terminator: terminator)
let logString = DCLogger.shared.dateString + " [DeepChordsSdk-\(String(format: "%03d", line))]: " + logs.map({ "\($0)" }).joined(separator: separator) + terminator
Self.logs.append(logString)
DCLogger.shared.saveLog(log: logString)
+ objc_sync_exit(DCLogger.shared)
}
- objc_sync_exit(DCLogger.shared)
-
-
}
private func saveLog(log:String){
}
public var isDebug:Bool = false{
didSet{
- StarburstConfig.shared().isDebug = false
+ StarburstConfig.shared().isDebug = isDebug
DCLogger.isDebug = isDebug
}
}
DCBleManager.shared.sendToStarburst(data)
}
StarburstSdk.shared().bleRecordStateCallback = {[weak self] state in
-
+ DCLogger.debug("录音状态改变",state)
}
}
/* Begin PBXBuildFile section */
3F469CB12F0B539600121DBD /* DeepChordsSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D54E0EF2F0A61E600A80EE7 /* DeepChordsSdk.framework */; };
3F8F58292EBCA2B800FFC00B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3F8F58282EBCA2B800FFC00B /* PrivacyInfo.xcprivacy */; };
+ 3FD1A2952F0BCACB00A62733 /* JLAudioUnitKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FD1A2912F0BC9D800A62733 /* JLAudioUnitKit.xcframework */; };
+ 3FD1A2962F0BCACB00A62733 /* JLAudioUnitKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3FD1A2912F0BC9D800A62733 /* JLAudioUnitKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
644D1BB5EDD3EA1D77C57786 /* Pods_DeepChordsSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 248D8B53B5A178ACA5867275 /* Pods_DeepChordsSdk.framework */; };
F2E449CAD5874BE652A69B50 /* Pods_ShenYin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3862D5561144E20721225B96 /* Pods_ShenYin.framework */; };
/* End PBXBuildFile section */
dstPath = "";
dstSubfolderSpec = 10;
files = (
+ 3FD1A2962F0BCACB00A62733 /* JLAudioUnitKit.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
3F7602EA2EFA891200588C18 /* DeepChordsSdk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DeepChordsSdk.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3F887B9C2E38D8DC00C98658 /* ShenYin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShenYin.app; sourceTree = BUILT_PRODUCTS_DIR; };
3F8F58282EBCA2B800FFC00B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
+ 3FD1A2912F0BC9D800A62733 /* JLAudioUnitKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = JLAudioUnitKit.xcframework; path = framework/JLAudioUnitKit.xcframework; sourceTree = "<group>"; };
5A92F7DC2CB6E6E69D2AB23E /* Pods-DeepChordsSdk.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DeepChordsSdk.debug.xcconfig"; path = "Target Support Files/Pods-DeepChordsSdk/Pods-DeepChordsSdk.debug.xcconfig"; sourceTree = "<group>"; };
5F5DE5910965D53C2B3405B0 /* Pods-ShenYin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShenYin.debug.xcconfig"; path = "Target Support Files/Pods-ShenYin/Pods-ShenYin.debug.xcconfig"; sourceTree = "<group>"; };
CB933A7969ADF81D59503790 /* Pods-LingQi.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LingQi.debug.xcconfig"; path = "Target Support Files/Pods-LingQi/Pods-LingQi.debug.xcconfig"; sourceTree = "<group>"; };
files = (
3F469CB12F0B539600121DBD /* DeepChordsSdk.framework in Frameworks */,
F2E449CAD5874BE652A69B50 /* Pods_ShenYin.framework in Frameworks */,
+ 3FD1A2952F0BCACB00A62733 /* JLAudioUnitKit.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
676B86A4BA2639E8694D56B4 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 3FD1A2912F0BC9D800A62733 /* JLAudioUnitKit.xcframework */,
3F7602EA2EFA891200588C18 /* DeepChordsSdk.framework */,
3862D5561144E20721225B96 /* Pods_ShenYin.framework */,
248D8B53B5A178ACA5867275 /* Pods_DeepChordsSdk.framework */,
class DcStereo:NSObject{
static let shared = DcStereo()
+ var fileData = Data()
var recordFinish:((String)->Void)?
var stereoNeedReloadData:((String,Int)->Void)?
codeCallback?(code)
}
DCCallRecord.shared.opusCallBack = { data in
- OpusManager.shared().write(data)
+// OpusManager.shared().write(data)
+ OpusTools.shared.inputData(data)
}
DCCallRecord.shared.filePathCallBack = { [weak self] path in
guard let self = self else{return}
// if selfText.isEmpty && otherText.isEmpty {
// return
// }
- guard let path = path,path.isNotEmpty else{
- return
- }
- let path1 = OpusTool.opusFile(toWAVFile: path)
+// guard let path = path,path.isNotEmpty else{
+// return
+// }
+
+// YLToos.saveToFile(data: fileData, path: DCFolder.cacheDirectory() + "/11123.pcm")
+// Logger.debug("文件路径pcm ==",DCFolder.cacheDirectory() + "/11123.pcm")
+// let path1 = OpusTool.opusStereoFile(toWAVFile: path)
+ let path1 = OpusTools.pcmTowav(fileData) ?? ""
guard let wavPath = DCFolder.recordFileResave(srcPath: path1) else{return}
Logger.debug("文件路径wav ==",wavPath)
recordFinish?(wavPath)
+ fileData = Data()
+
}
- //设置解码参数
- OpusManager.shared().setupSampleRate(16000, frameRate: 80)
- //opus转成pcm,发送给sdk
- OpusManager.shared().pcmDataCallback = { data in
+// //设置解码参数
+// OpusManager.shared().setupSampleRate(16000, frameRate: 80)
+// //opus转成pcm,发送给sdk
+// OpusManager.shared().pcmDataCallback = { data in
+// self.data.append(data)
+// DCCallRecord.shared.inputPcm(data: data)
+// }
+
+ OpusTools.shared.output = { data in
+ self.fileData.append(data)
DCCallRecord.shared.inputPcm(data: data)
}
}
func setRecordLanage(_ language:CCAiLanguage){
- DCBleRecord.shared.fromLanguage = language.value
+ DCCallRecord.shared.fromLanguage = language.value
}
func recordStart(){
selfText = ""
otherText = ""
+ fileData = Data()
setupSdk()
if !CCCall.shared.isInCall {
callRecordNotOnCall?()
DCCallRecord.shared.start()
}
func recordStop(){
- DCBleRecord.shared.stop()
+ DCCallRecord.shared.stop()
}
}
--- /dev/null
+//
+// OpusTools.swift
+// JLAudioUnitKitDemo
+//
+// Created by liangshi on 2026/1/5.
+//
+import JLAudioUnitKit
+
+class OpusTools:NSObject, JLOpusDecoderDelegate {
+ static let shared = OpusTools()
+ private var opusDecoder:JLOpusDecoder!
+ private var format = JLOpusFormat.defaultFormats()
+
+ var output:((Data)->Void)?
+
+ override init(){
+ super.init()
+ format.sampleRate = 16000
+ format.hasDataHeader = false
+ format.channels = 2
+ format.dataSize = 80
+ self.opusDecoder = JLOpusDecoder(decoder: format, delegate: self)
+
+// opusDecoder.resetOpusFramet(format)
+ }
+
+ func inputData(_ data:Data){
+ self.opusDecoder.opusDecoderInputData(data)
+
+ }
+
+ func opusDecoder(_ decoder: JLOpusDecoder, data: Data?, error: (any Error)?) {
+ if let data = data{
+ output?(data)
+ }
+ }
+
+}
+
+
+extension OpusTools{
+ static func pcmTowav(_ data:Data)->String?{
+ do{
+ let path = DCFolder.cacheDirectory() + "/\(Date().timeIntervalSince1970).wav"
+ try JLPcmToWav.convertPCMData(data, toWAVFile: path, sampleRate: 16000, numChannels: 2, bitsPerSample: 16)
+ return path
+ }
+ catch{
+ return nil
+ }
+ }
+}
@interface OpusTool : NSObject
+ (NSString*)opusFileToWAVFile:(NSString*)path;
+
++ (NSString*)opusStereoFileToWAVFile:(NSString*)path;
@end
NS_ASSUME_NONNULL_END
return nil;
}
++ (NSString*)opusStereoFileToWAVFile:(NSString*)path {
+ NSString *dataFilePath = [path stringByDeletingLastPathComponent];
+ // 获取当前时间
+ NSDate *currentDate = [NSDate date];
+ // 创建日期格式化器
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setDateFormat:@"yyyyMMddHHmmssSSS"]; // 设置时间格式
+ // 将当前时间格式化为字符串
+ NSString *timestamp = [dateFormatter stringFromDate:currentDate];
+ // 生成文件名
+ NSString *fileName = [NSString stringWithFormat:@"%@_%@.%@", @"output", timestamp, @"pcm"];
+ NSString *filePath = [dataFilePath stringByAppendingPathComponent:fileName];
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ //NSLog(@"remove file : %@", filePath);
+ }
+ NSString *txt = @"";
+ [txt writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
+ [OpusUnit opusSetSampleRate:16000 frameRate:80 Channels:2 isHeader:NO];
+ // 调用解码方法,输入 Opus文件路径 与 PCM文件路径
+ int result = [OpusUnit opusDecodeOPUS:path PCM:filePath];
+ if (result == 0) {
+ // 删除opus文件
+ [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
+ // 解码成功
+ NSURL *url = [[NSURL alloc] initFileURLWithPath:filePath];
+ NSData *pcmData = [[NSData alloc] initWithContentsOfURL:url];
+ // 生成文件名
+ NSString *wavFileName = [NSString stringWithFormat:@"%@_%@.%@", @"output", timestamp, @"wav"];
+ NSURL *output = [[NSURL alloc] initFileURLWithPath: [dataFilePath stringByAppendingPathComponent:wavFileName]];
+ [self writePCMToWAV:pcmData sampleRate:16000 numChannels:2 bitsPerSample:16 outputURL:output];
+ // 删除pcm文件
+ [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
+
+ return output.path;
+ }
+ return nil;
+}
+
+ (void)writePCMToWAV:(NSData *)pcmData
sampleRate:(UInt32)sampleRate
numChannels:(UInt16)numChannels
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>AvailableLibraries</key>
+ <array>
+ <dict>
+ <key>BinaryPath</key>
+ <string>JLAudioUnitKit.framework/JLAudioUnitKit</string>
+ <key>LibraryIdentifier</key>
+ <string>ios-arm64</string>
+ <key>LibraryPath</key>
+ <string>JLAudioUnitKit.framework</string>
+ <key>SupportedArchitectures</key>
+ <array>
+ <string>arm64</string>
+ </array>
+ <key>SupportedPlatform</key>
+ <string>ios</string>
+ </dict>
+ <dict>
+ <key>BinaryPath</key>
+ <string>JLAudioUnitKit.framework/JLAudioUnitKit</string>
+ <key>LibraryIdentifier</key>
+ <string>ios-x86_64-simulator</string>
+ <key>LibraryPath</key>
+ <string>JLAudioUnitKit.framework</string>
+ <key>SupportedArchitectures</key>
+ <array>
+ <string>x86_64</string>
+ </array>
+ <key>SupportedPlatform</key>
+ <string>ios</string>
+ <key>SupportedPlatformVariant</key>
+ <string>simulator</string>
+ </dict>
+ </array>
+ <key>CFBundlePackageType</key>
+ <string>XFWK</string>
+ <key>XCFrameworkFormatVersion</key>
+ <string>1.0</string>
+</dict>
+</plist>
--- /dev/null
+//
+// JLAudioUnitKit.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for JLAudioUnitKit.
+FOUNDATION_EXPORT double JLAudioUnitKitVersionNumber;
+
+//! Project version string for JLAudioUnitKit.
+FOUNDATION_EXPORT const unsigned char JLAudioUnitKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <JLAudioUnitKit/PublicHeader.h>
+
+#import <JLAudioUnitKit/JLSpeexDecoder.h>
+#import <JLAudioUnitKit/JLOpusDecoder.h>
+#import <JLAudioUnitKit/JLOpusEncoder.h>
+#import <JLAudioUnitKit/JLOpusFormat.h>
+#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
+#import <JLAudioUnitKit/JLPcmToWtg.h>
+#import <JLAudioUnitKit/JLPcmToWav.h>
+#import <JLAudioUnitKit/JLAudioUnitPlayer.h>
+#import <JLAudioUnitKit/JLOpusToOgg.h>
--- /dev/null
+//
+// JLAudioUnitPlayer.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/4/16.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <AudioToolbox/AudioToolbox.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) {
+ JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer)
+ JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue)
+};
+
+@class JLAudioUnitPlayer;
+
+@protocol JLAudioPlayerDelegate <NSObject>
+@optional
+// 播放进度更新(秒)
+- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration;
+// 播放完成
+- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player;
+// 播放错误
+- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error;
+@end
+
+@interface JLAudioUnitPlayer : NSObject
+
+@property (nonatomic, weak) id<JLAudioPlayerDelegate> delegate;
+@property (nonatomic, readonly) NSTimeInterval duration; // 总时长(文件模式下有效)
+@property (nonatomic, readonly) NSTimeInterval currentTime;
+@property (nonatomic, readonly) BOOL isPlaying;
+
+#pragma mark - 初始化方法
+
+/// 文件播放初始化(MP3/WAV/AAC)
+- (instancetype)initWithAudioFile:(NSString *)filePath;
+
+/// PCM 流播放初始化(需指定格式)
+- (instancetype)initWithPCMFormat:(AudioStreamBasicDescription)format;
+
+#pragma mark - 播放控制
+- (void)play;
+- (void)pause;
+- (void)stop;
+- (void)seekToTime:(NSTimeInterval)time; // 仅文件模式有效
+
+#pragma mark - PCM 流式输入(仅 PCM 模式有效)
+- (void)appendPCMData:(NSData *)pcmData;
+- (void)endPCMStream; // 结束 PCM 流输入
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusDecoder.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <JLAudioUnitKit/JLOpusFormat.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLOpusDecoder;
+
+/// Opus 解码代理
+@protocol JLOpusDecoderDelegate <NSObject>
+
+/// Opus 数据解码
+/// - Parameters:
+/// - decoder: 解码器
+/// - data: pcm 数据
+/// - error: 错误信息
+-(void)opusDecoder:(JLOpusDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLOpusDecoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Opus 解码
+@interface JLOpusDecoder : NSObject
+
+/// 数据格式参数
+@property (nonatomic, strong) JLOpusFormat *opusFormat;
+
+/// 代理委托
+@property(nonatomic, weak) id<JLOpusDecoderDelegate> delegate;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameters:
+/// - format: 解码格式
+/// - delegate: 代理
+- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id<JLOpusDecoderDelegate>)delegate;
+
+/// 重置解码格式
+/// - Parameter format: 解码格式
+-(void)resetOpusFramet:(JLOpusFormat *)format;
+
+/// 输入 Opus 数据
+/// - Parameter data: Opus 数据
+-(void)opusDecoderInputData:(NSData *)data;
+
+/// 解码文件
+/// - Parameters:
+/// - input: opus 文件
+/// - outPut: 输出路径
+/// - result: 结果回调
+-(void)opusDecodeFile:(NSString *)input outPut:(NSString *_Nullable)outPut Resoult:(JLOpusDecoderConvertBlock _Nullable)result;
+
+/// 释放
+-(void)opusOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusEncodeConfig.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/8/27.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, JLOpusEncoderBandwidth) {
+ ///(4kHz bandpass)
+ JLOpusEncoderBandwidthNarrowband = 1101, // 对应 OPUS_BANDWIDTH_NARROWBAND
+ /// (6kHz bandpass)
+ JLOpusEncoderBandwidthMediumband = 1102, // 对应 OPUS_BANDWIDTH_MEDIUMBAND
+ /// (8kHz bandpass)
+ JLOpusEncoderBandwidthWideband = 1103, // 对应 OPUS_BANDWIDTH_WIDEBAND
+ /// (12kHz bandpass)
+ JLOpusEncoderBandwidthSuperwideband = 1104,// 对应 OPUS_BANDWIDTH_SUPERWIDEBAND
+ /// (20kHz bandpass)
+ JLOpusEncoderBandwidthFullband = 1105 // 对应 OPUS_BANDWIDTH_FULLBAND
+};
+
+@interface JLOpusEncodeConfig : NSObject
+
+/// 采样率
+@property (nonatomic, assign) int sampleRate;
+
+/// 单/双声道
+@property (nonatomic, assign) int channels;
+
+/// 帧长度 20ms 默认值
+@property (nonatomic, assign) int frameDuration;
+
+/// 帧长度
+@property (nonatomic, assign) int frameSize;
+
+/// BitRate
+@property (nonatomic, assign) int bitRate;
+
+/// 可变比特率开关,默认 NO(使用 CBR)
+@property (nonatomic, assign) BOOL useVBR;
+
+/// VBR 限制模式,默认 NO
+@property (nonatomic, assign) BOOL constrainedVBR;
+
+/// 编码复杂度 0~10,默认 5
+@property (nonatomic, assign) int complexity;
+
+/// 强制输出声道数 -1: 自适应, 1: 单声道, 2: 双声道, 默认 -1
+@property (nonatomic, assign) int forceChannels;
+
+/// 是否启用 DTX(静音段不发送数据),默认 NO
+@property (nonatomic, assign) BOOL useDTX;
+
+/// 网络丢包百分比,默认 0
+@property (nonatomic, assign) int packetLossPercent;
+
+/// 最大带宽限制(OPUS_BANDWIDTH_*),默认 FULLBAND
+@property (nonatomic, assign) int bandwidth;
+
+/// PCM 输入有效位深,默认 16
+@property (nonatomic, assign) int lsbDepth;
+
+/// 专家模式下帧时长(ms),默认与 frameDuration 一致
+@property (nonatomic, assign) int expertFrameDuration;
+
+/// 是否启用数据头
+/// 默认是启用的
+@property (nonatomic, assign) BOOL hasDataHeader;
+
+/// 创建默认编码配置
++ (instancetype)defaultConfig;
+
+/// 获取默认配置
+/// 杰理的无头配置
++ (instancetype)defaultJL;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusEncoder.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLOpusEncoder;
+
+/// Opus 编码代理
+@protocol JLOpusEncoderDelegate <NSObject>
+
+/// PCM 数据编码
+/// - Parameters:
+/// - encoder: 解码器
+/// - data: opus 数据
+/// - error: 错误信息
+-(void)opusEncoder:(JLOpusEncoder *)encoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLOpusEncoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Opus 编码
+@interface JLOpusEncoder : NSObject
+
+/// 音频格式
+@property (nonatomic, strong) JLOpusEncodeConfig *opusFormat;
+
+/// 代理
+@property (nonatomic, weak) id<JLOpusEncoderDelegate> delegate;
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameters:
+/// - config: 音频格式
+/// - delegate: 代理
+-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id<JLOpusEncoderDelegate>)delegate;
+
+/// PCM 数据
+/// - Parameter data: PCM 数据
+-(void)opusEncodeData:(NSData *)data;
+
+/// PCM 文件转换成 Opus 文件
+/// - Parameters:
+/// - pcmPath: PCM 文件存放路径
+/// - outPut: 文件输出路径
+/// - result: 结果回调
+-(void)opusEncodeFile:(NSString *)pcmPath outPut:(NSString *_Nullable)outPut Resoult:(JLOpusEncoderConvertBlock _Nullable)result;
+
+/// 释放
+-(void)opusOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusOptions.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+#define OPUS_JL_MAX_FRAME_SIZE 48000 * 2
+#define OPUS_JL_MAX_PACKET_SIZE 1500
+
+
+@interface JLOpusFormat : NSObject
+
+/// 采样率
+@property (nonatomic, assign) int sampleRate;
+
+/// 单/双声道
+@property (nonatomic, assign) int channels;
+
+/// 帧长度 20ms 默认值
+@property (nonatomic, assign) int frameDuration;
+
+/// BitRate
+@property (nonatomic, assign) int bitRate;
+
+/// 数据帧大小
+@property (nonatomic, assign, readonly) int frameSize;
+
+/// 数据大小
+@property (nonatomic, assign) int dataSize;
+
+/// 是否包含数据头部
+@property (nonatomic, assign) BOOL hasDataHeader;
+
+/// 是否开启slik语音优化
+@property (nonatomic, assign) BOOL usVoipSlik;
+
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 默认配置
+/// sampleRate: 16000
+/// channels: 1
+/// frameDuration: 20
+/// dataSize: 40
+/// frameSize: 320
+/// hasDataHeader: YES
+/// bitRate: 16000
+/// usVoipSlik: false
++(JLOpusFormat*)defaultFormats;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusToOgg.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/5/16.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^JLOpusToOggConvertBlock)(NSData *_Nullable oggData, BOOL isLast, NSError *_Nullable error);
+
+/// 将Opus数据转换为Ogg数据
+@interface JLOpusToOgg : NSObject
+
+/// 转码内容的回调
+@property(nonatomic,strong)JLOpusToOggConvertBlock _Nullable convertBlock;
+
+/// 帧长
+/// - Parameter frameLen: 帧长
+/// 默认值 40
+- (instancetype)initWithFrameLength:(uint32_t)frameLen;
+
+/// 开始流
+-(void)startStream;
+
+/// 添加Opus数据
+/// - Parameter opusData: 符合杰理的无头裸 opus 数据
+- (void)appendOpusData:(NSData *)opusData;
+
+/// 关闭流
+- (void)closeStream;
+
+
+/// 将Opus数据转换为Ogg数据
+/// opus 必须为 16000
+/// opus 必须是 单声道
+/// - Parameters:
+/// - opusData: 符合杰理的无头裸 opus 数据
+/// - frameLen: 帧长 公版默认帧长是 40
+/// - duration: 转换后的时长
+/// - error: 错误
++ (NSData *_Nullable)convertOpusDataToOgg:(NSData *)opusData
+ frameLen:(uint32_t)frameLen
+ duration:(double *)duration
+ error:(NSError *__autoreleasing _Nullable *)error ;
+
+/// 将Opus文件转换为Ogg文件
+/// 符合杰理的无头裸 opus 文件
+/// opus 必须为 16000
+/// opus 必须是 单声道
+/// - Parameters:
+/// - opusFilePath: opus文件路径
+/// - oggFilePath: ogg文件路径
+/// - frameLen: 帧长 公版默认帧长是 40
+/// - duration: 转换后的时长
++(void)convertOpusFileToOgg:(NSString *)opusFilePath
+ oggFilePath:(NSString *)oggFilePath
+ frameLen:(uint32_t)frameLen
+ duration:(double *)duration;
+
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLPcmToWav.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/4/15.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// PCM 转 WAV
+@interface JLPcmToWav : NSObject
+
+#pragma mark - 流式编码接口
+
+/// 初始化流式编码器
+/// @param outputPath 输出文件路径
+/// @param sampleRate 采样率(如 16000)
+/// @param numChannels 声道数(1 或 2)
+/// @param bitsPerSample 位深(16)
+- (instancetype)initWithOutputPath:(NSString *)outputPath
+ sampleRate:(uint32_t)sampleRate
+ numChannels:(uint16_t)numChannels
+ bitsPerSample:(uint16_t)bitsPerSample;
+
+/// 追加 PCM 数据(需确保数据格式与初始化参数一致)
+/// @param pcmData PCM 数据块
+/// @param error 错误信息
+- (BOOL)appendPCMData:(NSData *)pcmData error:(NSError **)error;
+
+/// 完成编码并关闭文件(必须调用以更新 WAV 头)
+/// @param error 错误信息
+- (BOOL)finishWithError:(NSError **)error;
+
+#pragma mark - 一次性编码接口(便捷方法)
+
+/// 将 PCM 数据一次性转为 WAV 文件
+/// @param pcmData PCM 数据块
+/// @param outputPath 输出文件路径
+/// @param sampleRate 采样率(如 16000)
+/// @param numChannels 声道数(1 或 2)
+/// @param bitsPerSample 位深(16)
+/// @param error 错误信息
++ (BOOL)convertPCMData:(NSData *)pcmData
+ toWAVFile:(NSString *)outputPath
+ sampleRate:(uint32_t)sampleRate
+ numChannels:(uint16_t)numChannels
+ bitsPerSample:(uint16_t)bitsPerSample
+ error:(NSError **)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLPcmToWtg.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/1/21.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface JLPcm2WtgModel : NSObject <NSCopying>
+
+/// 音频文件
+@property (nonatomic, copy) NSString *pcmPath;
+
+/// 输出文件
+@property (nonatomic, copy) NSString *wtgPath;
+
+@end
+
+
+/// JLPcmToWtgProtocol
+@protocol JLPcmToWtgDelegate <NSObject>
+
+/// 音频转码完成
+/// - Parameter model: 音频转码模型
+- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model;
+
+@end
+
+/// 音频转码
+/// PCM 转 WTG
+@interface JLPcmToWtg : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// 音频转码初始化
+/// - Parameter delegate: 音频转码代理
+- (instancetype)initWithDelegate:(id<JLPcmToWtgDelegate>)delegate;
+
+/// 音频转码
+/// 当前接口模型只限制 PCM 文件格式为:8k 16bit
+/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件
+/// - Parameter model: 音频转码模型
+-(void)convertPcmToWtg:(JLPcm2WtgModel *)model;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLSpeexUnit.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLSpeexDecoder;
+
+@protocol JLSpeexDelegate <NSObject>
+
+/// Speex 数据解码
+/// - Parameters:
+/// - decoder: 解码器
+/// - data: PCM 数据
+/// - error: 错误信息
+- (void)speexDecoder:(JLSpeexDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLSpeexConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Speex 数据解码
+@interface JLSpeexDecoder : NSObject
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameter delegate: 委托
+-(instancetype)initWithDelegate:(id<JLSpeexDelegate>)delegate;
+
+/// 委托
+@property (nonatomic, weak) id<JLSpeexDelegate> delegate;
+
+/// 输入 Speex 数据
+/// - Parameter data: Speex 数据
+-(void)speexInputData:(NSData*)data;
+
+/// 将 Speex 转换为 PCM
+/// - Parameters:
+/// - filePath: Speex 路径
+/// - opPath: 输出路径
+/// - result: 结果回调
+-(void)speexConvertToPcm:(NSString *)filePath outPutFilePath:(NSString * _Nullable)opPath Result:(JLSpeexConvertBlock)result;
+
+/// 释放对象
+-(void)speexOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+framework module JLAudioUnitKit {
+ umbrella header "JLAudioUnitKit.h"
+ export *
+
+ module * { export * }
+}
--- /dev/null
+DESCRIPTION\r
+-----------\r
+This package includes the files needed to build the fixed point implementation\r
+of the ITU-T G.729 Appendix IV.\r
+\r
+DIFFERENCES WITH G.729B\r
+-----------------------\r
+The ANSI-C source code simulating the bit-exact, fixed point simulation \r
+software of enhanced VAD is integrated into the G.729B source code. \r
+The modification to the G.729B source code updated files of coder.c, \r
+cod_ld8k.c, bits.c, dec_sid.c, dtx.c, qsidgain.c, qsidlsf.c, tab_dtx.c, \r
+tab_dtx.h, ld8k.h, deleted vad.c/h and integrated the enhanced VAD functions\r
+into the G.729B project. The modification also replaced the original basic \r
+operators used in the original G.729B source code by the basic operators of \r
+ITU-T Software Tool Library STL2005.\r
+\r
+COMPILATION\r
+-----------\r
+For UNIX systems the following makefiles are provided\r
+\r
+ coder.mak\r
+ decoder.mak\r
+\r
+Edit the makefiles coder.mak and decoder.mak to set the proper options\r
+for your system.\r
+The command to compile and link all code on a UNIX system is\r
+\r
+ make -f coder.mak\r
+ make -f decoder.mak\r
+\r
+For other platforms, the *.mak files can be used to work out the\r
+compilation procedures.\r
+\r
+USAGE\r
+-----\r
+The command line instruction for the encoder is as follows:\r
+ coder inputfile bitstreamfile dtx_option mode x \r
+where:\r
+ inputfile : 8 kHz sampled data file containing 16 bit linear PCM signal \r
+ bitstreamfile: binary file containing bitstream \r
+ dtx_option : = 1 : DTX enabled 0 : DTX disabled\r
+ mode X : Run encoder with enhanced VAD in operating point X, \r
+ X=0 or 1 or 2 representing balanced or quality-preferred \r
+ or bandwidth saving operating point, respectively \r
+\r
+The command line for the decoder is as follows:\r
+ decoder bitstreamfile outputfile\r
+where:\r
+ bitstreamfile: binary file containing bitstream\r
+ outputfile : 8 kHz sampled data file containing 16 bit linear PCM signal\r
+\r
+\r
--- /dev/null
+//
+// JLAudioUnitKit.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for JLAudioUnitKit.
+FOUNDATION_EXPORT double JLAudioUnitKitVersionNumber;
+
+//! Project version string for JLAudioUnitKit.
+FOUNDATION_EXPORT const unsigned char JLAudioUnitKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <JLAudioUnitKit/PublicHeader.h>
+
+#import <JLAudioUnitKit/JLSpeexDecoder.h>
+#import <JLAudioUnitKit/JLOpusDecoder.h>
+#import <JLAudioUnitKit/JLOpusEncoder.h>
+#import <JLAudioUnitKit/JLOpusFormat.h>
+#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
+#import <JLAudioUnitKit/JLPcmToWtg.h>
+#import <JLAudioUnitKit/JLPcmToWav.h>
+#import <JLAudioUnitKit/JLAudioUnitPlayer.h>
+#import <JLAudioUnitKit/JLOpusToOgg.h>
--- /dev/null
+//
+// JLAudioUnitPlayer.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/4/16.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <AudioToolbox/AudioToolbox.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) {
+ JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer)
+ JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue)
+};
+
+@class JLAudioUnitPlayer;
+
+@protocol JLAudioPlayerDelegate <NSObject>
+@optional
+// 播放进度更新(秒)
+- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration;
+// 播放完成
+- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player;
+// 播放错误
+- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error;
+@end
+
+@interface JLAudioUnitPlayer : NSObject
+
+@property (nonatomic, weak) id<JLAudioPlayerDelegate> delegate;
+@property (nonatomic, readonly) NSTimeInterval duration; // 总时长(文件模式下有效)
+@property (nonatomic, readonly) NSTimeInterval currentTime;
+@property (nonatomic, readonly) BOOL isPlaying;
+
+#pragma mark - 初始化方法
+
+/// 文件播放初始化(MP3/WAV/AAC)
+- (instancetype)initWithAudioFile:(NSString *)filePath;
+
+/// PCM 流播放初始化(需指定格式)
+- (instancetype)initWithPCMFormat:(AudioStreamBasicDescription)format;
+
+#pragma mark - 播放控制
+- (void)play;
+- (void)pause;
+- (void)stop;
+- (void)seekToTime:(NSTimeInterval)time; // 仅文件模式有效
+
+#pragma mark - PCM 流式输入(仅 PCM 模式有效)
+- (void)appendPCMData:(NSData *)pcmData;
+- (void)endPCMStream; // 结束 PCM 流输入
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusDecoder.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <JLAudioUnitKit/JLOpusFormat.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLOpusDecoder;
+
+/// Opus 解码代理
+@protocol JLOpusDecoderDelegate <NSObject>
+
+/// Opus 数据解码
+/// - Parameters:
+/// - decoder: 解码器
+/// - data: pcm 数据
+/// - error: 错误信息
+-(void)opusDecoder:(JLOpusDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLOpusDecoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Opus 解码
+@interface JLOpusDecoder : NSObject
+
+/// 数据格式参数
+@property (nonatomic, strong) JLOpusFormat *opusFormat;
+
+/// 代理委托
+@property(nonatomic, weak) id<JLOpusDecoderDelegate> delegate;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameters:
+/// - format: 解码格式
+/// - delegate: 代理
+- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id<JLOpusDecoderDelegate>)delegate;
+
+/// 重置解码格式
+/// - Parameter format: 解码格式
+-(void)resetOpusFramet:(JLOpusFormat *)format;
+
+/// 输入 Opus 数据
+/// - Parameter data: Opus 数据
+-(void)opusDecoderInputData:(NSData *)data;
+
+/// 解码文件
+/// - Parameters:
+/// - input: opus 文件
+/// - outPut: 输出路径
+/// - result: 结果回调
+-(void)opusDecodeFile:(NSString *)input outPut:(NSString *_Nullable)outPut Resoult:(JLOpusDecoderConvertBlock _Nullable)result;
+
+/// 释放
+-(void)opusOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusEncodeConfig.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/8/27.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, JLOpusEncoderBandwidth) {
+ ///(4kHz bandpass)
+ JLOpusEncoderBandwidthNarrowband = 1101, // 对应 OPUS_BANDWIDTH_NARROWBAND
+ /// (6kHz bandpass)
+ JLOpusEncoderBandwidthMediumband = 1102, // 对应 OPUS_BANDWIDTH_MEDIUMBAND
+ /// (8kHz bandpass)
+ JLOpusEncoderBandwidthWideband = 1103, // 对应 OPUS_BANDWIDTH_WIDEBAND
+ /// (12kHz bandpass)
+ JLOpusEncoderBandwidthSuperwideband = 1104,// 对应 OPUS_BANDWIDTH_SUPERWIDEBAND
+ /// (20kHz bandpass)
+ JLOpusEncoderBandwidthFullband = 1105 // 对应 OPUS_BANDWIDTH_FULLBAND
+};
+
+@interface JLOpusEncodeConfig : NSObject
+
+/// 采样率
+@property (nonatomic, assign) int sampleRate;
+
+/// 单/双声道
+@property (nonatomic, assign) int channels;
+
+/// 帧长度 20ms 默认值
+@property (nonatomic, assign) int frameDuration;
+
+/// 帧长度
+@property (nonatomic, assign) int frameSize;
+
+/// BitRate
+@property (nonatomic, assign) int bitRate;
+
+/// 可变比特率开关,默认 NO(使用 CBR)
+@property (nonatomic, assign) BOOL useVBR;
+
+/// VBR 限制模式,默认 NO
+@property (nonatomic, assign) BOOL constrainedVBR;
+
+/// 编码复杂度 0~10,默认 5
+@property (nonatomic, assign) int complexity;
+
+/// 强制输出声道数 -1: 自适应, 1: 单声道, 2: 双声道, 默认 -1
+@property (nonatomic, assign) int forceChannels;
+
+/// 是否启用 DTX(静音段不发送数据),默认 NO
+@property (nonatomic, assign) BOOL useDTX;
+
+/// 网络丢包百分比,默认 0
+@property (nonatomic, assign) int packetLossPercent;
+
+/// 最大带宽限制(OPUS_BANDWIDTH_*),默认 FULLBAND
+@property (nonatomic, assign) int bandwidth;
+
+/// PCM 输入有效位深,默认 16
+@property (nonatomic, assign) int lsbDepth;
+
+/// 专家模式下帧时长(ms),默认与 frameDuration 一致
+@property (nonatomic, assign) int expertFrameDuration;
+
+/// 是否启用数据头
+/// 默认是启用的
+@property (nonatomic, assign) BOOL hasDataHeader;
+
+/// 创建默认编码配置
++ (instancetype)defaultConfig;
+
+/// 获取默认配置
+/// 杰理的无头配置
++ (instancetype)defaultJL;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusEncoder.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLOpusEncoder;
+
+/// Opus 编码代理
+@protocol JLOpusEncoderDelegate <NSObject>
+
+/// PCM 数据编码
+/// - Parameters:
+/// - encoder: 解码器
+/// - data: opus 数据
+/// - error: 错误信息
+-(void)opusEncoder:(JLOpusEncoder *)encoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLOpusEncoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Opus 编码
+@interface JLOpusEncoder : NSObject
+
+/// 音频格式
+@property (nonatomic, strong) JLOpusEncodeConfig *opusFormat;
+
+/// 代理
+@property (nonatomic, weak) id<JLOpusEncoderDelegate> delegate;
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameters:
+/// - config: 音频格式
+/// - delegate: 代理
+-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id<JLOpusEncoderDelegate>)delegate;
+
+/// PCM 数据
+/// - Parameter data: PCM 数据
+-(void)opusEncodeData:(NSData *)data;
+
+/// PCM 文件转换成 Opus 文件
+/// - Parameters:
+/// - pcmPath: PCM 文件存放路径
+/// - outPut: 文件输出路径
+/// - result: 结果回调
+-(void)opusEncodeFile:(NSString *)pcmPath outPut:(NSString *_Nullable)outPut Resoult:(JLOpusEncoderConvertBlock _Nullable)result;
+
+/// 释放
+-(void)opusOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusOptions.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+#define OPUS_JL_MAX_FRAME_SIZE 48000 * 2
+#define OPUS_JL_MAX_PACKET_SIZE 1500
+
+
+@interface JLOpusFormat : NSObject
+
+/// 采样率
+@property (nonatomic, assign) int sampleRate;
+
+/// 单/双声道
+@property (nonatomic, assign) int channels;
+
+/// 帧长度 20ms 默认值
+@property (nonatomic, assign) int frameDuration;
+
+/// BitRate
+@property (nonatomic, assign) int bitRate;
+
+/// 数据帧大小
+@property (nonatomic, assign, readonly) int frameSize;
+
+/// 数据大小
+@property (nonatomic, assign) int dataSize;
+
+/// 是否包含数据头部
+@property (nonatomic, assign) BOOL hasDataHeader;
+
+/// 是否开启slik语音优化
+@property (nonatomic, assign) BOOL usVoipSlik;
+
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 默认配置
+/// sampleRate: 16000
+/// channels: 1
+/// frameDuration: 20
+/// dataSize: 40
+/// frameSize: 320
+/// hasDataHeader: YES
+/// bitRate: 16000
+/// usVoipSlik: false
++(JLOpusFormat*)defaultFormats;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLOpusToOgg.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/5/16.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^JLOpusToOggConvertBlock)(NSData *_Nullable oggData, BOOL isLast, NSError *_Nullable error);
+
+/// 将Opus数据转换为Ogg数据
+@interface JLOpusToOgg : NSObject
+
+/// 转码内容的回调
+@property(nonatomic,strong)JLOpusToOggConvertBlock _Nullable convertBlock;
+
+/// 帧长
+/// - Parameter frameLen: 帧长
+/// 默认值 40
+- (instancetype)initWithFrameLength:(uint32_t)frameLen;
+
+/// 开始流
+-(void)startStream;
+
+/// 添加Opus数据
+/// - Parameter opusData: 符合杰理的无头裸 opus 数据
+- (void)appendOpusData:(NSData *)opusData;
+
+/// 关闭流
+- (void)closeStream;
+
+
+/// 将Opus数据转换为Ogg数据
+/// opus 必须为 16000
+/// opus 必须是 单声道
+/// - Parameters:
+/// - opusData: 符合杰理的无头裸 opus 数据
+/// - frameLen: 帧长 公版默认帧长是 40
+/// - duration: 转换后的时长
+/// - error: 错误
++ (NSData *_Nullable)convertOpusDataToOgg:(NSData *)opusData
+ frameLen:(uint32_t)frameLen
+ duration:(double *)duration
+ error:(NSError *__autoreleasing _Nullable *)error ;
+
+/// 将Opus文件转换为Ogg文件
+/// 符合杰理的无头裸 opus 文件
+/// opus 必须为 16000
+/// opus 必须是 单声道
+/// - Parameters:
+/// - opusFilePath: opus文件路径
+/// - oggFilePath: ogg文件路径
+/// - frameLen: 帧长 公版默认帧长是 40
+/// - duration: 转换后的时长
++(void)convertOpusFileToOgg:(NSString *)opusFilePath
+ oggFilePath:(NSString *)oggFilePath
+ frameLen:(uint32_t)frameLen
+ duration:(double *)duration;
+
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLPcmToWav.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/4/15.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// PCM 转 WAV
+@interface JLPcmToWav : NSObject
+
+#pragma mark - 流式编码接口
+
+/// 初始化流式编码器
+/// @param outputPath 输出文件路径
+/// @param sampleRate 采样率(如 16000)
+/// @param numChannels 声道数(1 或 2)
+/// @param bitsPerSample 位深(16)
+- (instancetype)initWithOutputPath:(NSString *)outputPath
+ sampleRate:(uint32_t)sampleRate
+ numChannels:(uint16_t)numChannels
+ bitsPerSample:(uint16_t)bitsPerSample;
+
+/// 追加 PCM 数据(需确保数据格式与初始化参数一致)
+/// @param pcmData PCM 数据块
+/// @param error 错误信息
+- (BOOL)appendPCMData:(NSData *)pcmData error:(NSError **)error;
+
+/// 完成编码并关闭文件(必须调用以更新 WAV 头)
+/// @param error 错误信息
+- (BOOL)finishWithError:(NSError **)error;
+
+#pragma mark - 一次性编码接口(便捷方法)
+
+/// 将 PCM 数据一次性转为 WAV 文件
+/// @param pcmData PCM 数据块
+/// @param outputPath 输出文件路径
+/// @param sampleRate 采样率(如 16000)
+/// @param numChannels 声道数(1 或 2)
+/// @param bitsPerSample 位深(16)
+/// @param error 错误信息
++ (BOOL)convertPCMData:(NSData *)pcmData
+ toWAVFile:(NSString *)outputPath
+ sampleRate:(uint32_t)sampleRate
+ numChannels:(uint16_t)numChannels
+ bitsPerSample:(uint16_t)bitsPerSample
+ error:(NSError **)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLPcmToWtg.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2025/1/21.
+// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface JLPcm2WtgModel : NSObject <NSCopying>
+
+/// 音频文件
+@property (nonatomic, copy) NSString *pcmPath;
+
+/// 输出文件
+@property (nonatomic, copy) NSString *wtgPath;
+
+@end
+
+
+/// JLPcmToWtgProtocol
+@protocol JLPcmToWtgDelegate <NSObject>
+
+/// 音频转码完成
+/// - Parameter model: 音频转码模型
+- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model;
+
+@end
+
+/// 音频转码
+/// PCM 转 WTG
+@interface JLPcmToWtg : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// 音频转码初始化
+/// - Parameter delegate: 音频转码代理
+- (instancetype)initWithDelegate:(id<JLPcmToWtgDelegate>)delegate;
+
+/// 音频转码
+/// 当前接口模型只限制 PCM 文件格式为:8k 16bit
+/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件
+/// - Parameter model: 音频转码模型
+-(void)convertPcmToWtg:(JLPcm2WtgModel *)model;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// JLSpeexUnit.h
+// JLAudioUnitKit
+//
+// Created by EzioChan on 2024/11/14.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+@class JLSpeexDecoder;
+
+@protocol JLSpeexDelegate <NSObject>
+
+/// Speex 数据解码
+/// - Parameters:
+/// - decoder: 解码器
+/// - data: PCM 数据
+/// - error: 错误信息
+- (void)speexDecoder:(JLSpeexDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error;
+
+@end
+
+typedef void(^JLSpeexConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error);
+
+/// Speex 数据解码
+@interface JLSpeexDecoder : NSObject
+
+-(instancetype)init NS_UNAVAILABLE;
+
+/// 初始化
+/// - Parameter delegate: 委托
+-(instancetype)initWithDelegate:(id<JLSpeexDelegate>)delegate;
+
+/// 委托
+@property (nonatomic, weak) id<JLSpeexDelegate> delegate;
+
+/// 输入 Speex 数据
+/// - Parameter data: Speex 数据
+-(void)speexInputData:(NSData*)data;
+
+/// 将 Speex 转换为 PCM
+/// - Parameters:
+/// - filePath: Speex 路径
+/// - opPath: 输出路径
+/// - result: 结果回调
+-(void)speexConvertToPcm:(NSString *)filePath outPutFilePath:(NSString * _Nullable)opPath Result:(JLSpeexConvertBlock)result;
+
+/// 释放对象
+-(void)speexOnRelease;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+framework module JLAudioUnitKit {
+ umbrella header "JLAudioUnitKit.h"
+ export *
+
+ module * { export * }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>files</key>
+ <dict>
+ <key>Headers/JLAudioUnitKit.h</key>
+ <data>
+ hE2nr+4q2/NdSgBaayJc7ZoOXb0=
+ </data>
+ <key>Headers/JLAudioUnitPlayer.h</key>
+ <data>
+ QPMLV/XA8hjk39d+vT+S57t0fDU=
+ </data>
+ <key>Headers/JLOpusDecoder.h</key>
+ <data>
+ K1bYcaJ67prImNlGuldy8csTRHc=
+ </data>
+ <key>Headers/JLOpusEncodeConfig.h</key>
+ <data>
+ NArAew5QxnSJGsD0tRzQ2V7bHrY=
+ </data>
+ <key>Headers/JLOpusEncoder.h</key>
+ <data>
+ BkgtVe9kCM4Fkc9oH1tC3BNh5Kw=
+ </data>
+ <key>Headers/JLOpusFormat.h</key>
+ <data>
+ Du1x3VNk87NstmspRk7ElijuZrk=
+ </data>
+ <key>Headers/JLOpusToOgg.h</key>
+ <data>
+ fSJT6LcyylJHTHX1wsN3JHG2FxU=
+ </data>
+ <key>Headers/JLPcmToWav.h</key>
+ <data>
+ /BbZmqmWpWVBusXqjnzbjaBNCNg=
+ </data>
+ <key>Headers/JLPcmToWtg.h</key>
+ <data>
+ zoOdOFpIulND0F1OrfIXR1H0kh0=
+ </data>
+ <key>Headers/JLSpeexDecoder.h</key>
+ <data>
+ 5ecBKtD3BDGccLiKpTs0fS0CSgs=
+ </data>
+ <key>Info.plist</key>
+ <data>
+ Q2Tz1ljDQKvKxkn8afbgb/2RVnU=
+ </data>
+ <key>Modules/module.modulemap</key>
+ <data>
+ 2vYf0m+hGTOTapX6sliv/F1E1VI=
+ </data>
+ <key>readme.txt</key>
+ <data>
+ wijZX0Dr2OlODWS6XYbpeKabb7g=
+ </data>
+ </dict>
+ <key>files2</key>
+ <dict>
+ <key>Headers/JLAudioUnitKit.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ J5pq6Ed3fR+cQ7CGPXDoPl7BZzix7Cjf5yJZFRRvxqs=
+ </data>
+ </dict>
+ <key>Headers/JLAudioUnitPlayer.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ qBz0OA82HUH4kBtO8xiUw7Q4ohtiIvCSYfCqax5xjJY=
+ </data>
+ </dict>
+ <key>Headers/JLOpusDecoder.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ cnAj6Nt6vgRLoEH6QY3Q+gYpjcxGPN1OLLZbCgdLymI=
+ </data>
+ </dict>
+ <key>Headers/JLOpusEncodeConfig.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ qrPpsrXaNCvyxf0/gUZtvveL+ywsfgBUrMPFibjneOo=
+ </data>
+ </dict>
+ <key>Headers/JLOpusEncoder.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ PK+jl5IacMxuDEKkBwsjwpnGYxj/6Th3ICW4YQ7aRPA=
+ </data>
+ </dict>
+ <key>Headers/JLOpusFormat.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ Nm6vM9/EbFzyIWbsysu6EOO2S+vZVeplcop4NvoJaMA=
+ </data>
+ </dict>
+ <key>Headers/JLOpusToOgg.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ dNSvJJnED/6E1p7TYZ4zBzUnLDVsZ8NovuNYjge6T/w=
+ </data>
+ </dict>
+ <key>Headers/JLPcmToWav.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ l/SEQhSFh7ctq/pBV+Z7X+Z0/gz30eCw3s31otjMPho=
+ </data>
+ </dict>
+ <key>Headers/JLPcmToWtg.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ MwtbX3Sa0nytA+m11x+1rDvP4WvJnZLljshuT5B3WD0=
+ </data>
+ </dict>
+ <key>Headers/JLSpeexDecoder.h</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ pV0X3xn07Ps7wE+0EfL9Yu/08tp8Ug8s14iDigRU46o=
+ </data>
+ </dict>
+ <key>Modules/module.modulemap</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ akFCUM59aSAaRM/ADWQ138YT9GD3oAjcaTvTUo24Ml8=
+ </data>
+ </dict>
+ <key>readme.txt</key>
+ <dict>
+ <key>hash2</key>
+ <data>
+ +2GG9ORrf2yJnBe2joKWtyBZL9p2sCH8EXqK1GIgW0E=
+ </data>
+ </dict>
+ </dict>
+ <key>rules</key>
+ <dict>
+ <key>^.*</key>
+ <true/>
+ <key>^.*\.lproj/</key>
+ <dict>
+ <key>optional</key>
+ <true/>
+ <key>weight</key>
+ <real>1000</real>
+ </dict>
+ <key>^.*\.lproj/locversion.plist$</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>1100</real>
+ </dict>
+ <key>^Base\.lproj/</key>
+ <dict>
+ <key>weight</key>
+ <real>1010</real>
+ </dict>
+ <key>^version.plist$</key>
+ <true/>
+ </dict>
+ <key>rules2</key>
+ <dict>
+ <key>.*\.dSYM($|/)</key>
+ <dict>
+ <key>weight</key>
+ <real>11</real>
+ </dict>
+ <key>^(.*/)?\.DS_Store$</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>2000</real>
+ </dict>
+ <key>^.*</key>
+ <true/>
+ <key>^.*\.lproj/</key>
+ <dict>
+ <key>optional</key>
+ <true/>
+ <key>weight</key>
+ <real>1000</real>
+ </dict>
+ <key>^.*\.lproj/locversion.plist$</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>1100</real>
+ </dict>
+ <key>^Base\.lproj/</key>
+ <dict>
+ <key>weight</key>
+ <real>1010</real>
+ </dict>
+ <key>^Info\.plist$</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>20</real>
+ </dict>
+ <key>^PkgInfo$</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>20</real>
+ </dict>
+ <key>^embedded\.provisionprofile$</key>
+ <dict>
+ <key>weight</key>
+ <real>20</real>
+ </dict>
+ <key>^version\.plist$</key>
+ <dict>
+ <key>weight</key>
+ <real>20</real>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+DESCRIPTION\r
+-----------\r
+This package includes the files needed to build the fixed point implementation\r
+of the ITU-T G.729 Appendix IV.\r
+\r
+DIFFERENCES WITH G.729B\r
+-----------------------\r
+The ANSI-C source code simulating the bit-exact, fixed point simulation \r
+software of enhanced VAD is integrated into the G.729B source code. \r
+The modification to the G.729B source code updated files of coder.c, \r
+cod_ld8k.c, bits.c, dec_sid.c, dtx.c, qsidgain.c, qsidlsf.c, tab_dtx.c, \r
+tab_dtx.h, ld8k.h, deleted vad.c/h and integrated the enhanced VAD functions\r
+into the G.729B project. The modification also replaced the original basic \r
+operators used in the original G.729B source code by the basic operators of \r
+ITU-T Software Tool Library STL2005.\r
+\r
+COMPILATION\r
+-----------\r
+For UNIX systems the following makefiles are provided\r
+\r
+ coder.mak\r
+ decoder.mak\r
+\r
+Edit the makefiles coder.mak and decoder.mak to set the proper options\r
+for your system.\r
+The command to compile and link all code on a UNIX system is\r
+\r
+ make -f coder.mak\r
+ make -f decoder.mak\r
+\r
+For other platforms, the *.mak files can be used to work out the\r
+compilation procedures.\r
+\r
+USAGE\r
+-----\r
+The command line instruction for the encoder is as follows:\r
+ coder inputfile bitstreamfile dtx_option mode x \r
+where:\r
+ inputfile : 8 kHz sampled data file containing 16 bit linear PCM signal \r
+ bitstreamfile: binary file containing bitstream \r
+ dtx_option : = 1 : DTX enabled 0 : DTX disabled\r
+ mode X : Run encoder with enhanced VAD in operating point X, \r
+ X=0 or 1 or 2 representing balanced or quality-preferred \r
+ or bandwidth saving operating point, respectively \r
+\r
+The command line for the decoder is as follows:\r
+ decoder bitstreamfile outputfile\r
+where:\r
+ bitstreamfile: binary file containing bitstream\r
+ outputfile : 8 kHz sampled data file containing 16 bit linear PCM signal\r
+\r
+\r