2560 changed files with 336731 additions and 690218 deletions
@ -0,0 +1,2 @@ |
|||||
|
--- |
||||
|
BUNDLE_PATH: "vendor/bundle" |
||||
@ -0,0 +1,3 @@ |
|||||
|
source "https://rubygems.org" |
||||
|
gem 'cocoapods', '~> 1.16' |
||||
|
gem 'cocoapods-spm', '~> 0.1.15' |
||||
@ -0,0 +1,106 @@ |
|||||
|
GEM |
||||
|
remote: https://rubygems.org/ |
||||
|
specs: |
||||
|
CFPropertyList (3.0.7) |
||||
|
base64 |
||||
|
nkf |
||||
|
rexml |
||||
|
activesupport (6.1.7.10) |
||||
|
concurrent-ruby (~> 1.0, >= 1.0.2) |
||||
|
i18n (>= 1.6, < 2) |
||||
|
minitest (>= 5.1) |
||||
|
tzinfo (~> 2.0) |
||||
|
zeitwerk (~> 2.3) |
||||
|
addressable (2.8.7) |
||||
|
public_suffix (>= 2.0.2, < 7.0) |
||||
|
algoliasearch (1.27.5) |
||||
|
httpclient (~> 2.8, >= 2.8.3) |
||||
|
json (>= 1.5.1) |
||||
|
atomos (0.1.3) |
||||
|
base64 (0.2.0) |
||||
|
claide (1.1.0) |
||||
|
cocoapods (1.16.2) |
||||
|
addressable (~> 2.8) |
||||
|
claide (>= 1.0.2, < 2.0) |
||||
|
cocoapods-core (= 1.16.2) |
||||
|
cocoapods-deintegrate (>= 1.0.3, < 2.0) |
||||
|
cocoapods-downloader (>= 2.1, < 3.0) |
||||
|
cocoapods-plugins (>= 1.0.0, < 2.0) |
||||
|
cocoapods-search (>= 1.0.0, < 2.0) |
||||
|
cocoapods-trunk (>= 1.6.0, < 2.0) |
||||
|
cocoapods-try (>= 1.1.0, < 2.0) |
||||
|
colored2 (~> 3.1) |
||||
|
escape (~> 0.0.4) |
||||
|
fourflusher (>= 2.3.0, < 3.0) |
||||
|
gh_inspector (~> 1.0) |
||||
|
molinillo (~> 0.8.0) |
||||
|
nap (~> 1.0) |
||||
|
ruby-macho (>= 2.3.0, < 3.0) |
||||
|
xcodeproj (>= 1.27.0, < 2.0) |
||||
|
cocoapods-core (1.16.2) |
||||
|
activesupport (>= 5.0, < 8) |
||||
|
addressable (~> 2.8) |
||||
|
algoliasearch (~> 1.0) |
||||
|
concurrent-ruby (~> 1.1) |
||||
|
fuzzy_match (~> 2.0.4) |
||||
|
nap (~> 1.0) |
||||
|
netrc (~> 0.11) |
||||
|
public_suffix (~> 4.0) |
||||
|
typhoeus (~> 1.0) |
||||
|
cocoapods-deintegrate (1.0.5) |
||||
|
cocoapods-downloader (2.1) |
||||
|
cocoapods-plugins (1.0.0) |
||||
|
nap |
||||
|
cocoapods-search (1.0.1) |
||||
|
cocoapods-spm (0.1.15) |
||||
|
xcodeproj (>= 1.23.0) |
||||
|
cocoapods-trunk (1.6.0) |
||||
|
nap (>= 0.8, < 2.0) |
||||
|
netrc (~> 0.11) |
||||
|
cocoapods-try (1.2.0) |
||||
|
colored2 (3.1.2) |
||||
|
concurrent-ruby (1.3.5) |
||||
|
escape (0.0.4) |
||||
|
ethon (0.16.0) |
||||
|
ffi (>= 1.15.0) |
||||
|
ffi (1.17.2) |
||||
|
fourflusher (2.3.1) |
||||
|
fuzzy_match (2.0.4) |
||||
|
gh_inspector (1.1.3) |
||||
|
httpclient (2.9.0) |
||||
|
mutex_m |
||||
|
i18n (1.14.7) |
||||
|
concurrent-ruby (~> 1.0) |
||||
|
json (2.7.6) |
||||
|
minitest (5.25.4) |
||||
|
molinillo (0.8.0) |
||||
|
mutex_m (0.3.0) |
||||
|
nanaimo (0.4.0) |
||||
|
nap (1.1.0) |
||||
|
netrc (0.11.0) |
||||
|
nkf (0.2.0) |
||||
|
public_suffix (4.0.7) |
||||
|
rexml (3.4.1) |
||||
|
ruby-macho (2.5.1) |
||||
|
typhoeus (1.4.1) |
||||
|
ethon (>= 0.9.0) |
||||
|
tzinfo (2.0.6) |
||||
|
concurrent-ruby (~> 1.0) |
||||
|
xcodeproj (1.27.0) |
||||
|
CFPropertyList (>= 2.3.3, < 4.0) |
||||
|
atomos (~> 0.1.3) |
||||
|
claide (>= 1.0.2, < 2.0) |
||||
|
colored2 (~> 3.1) |
||||
|
nanaimo (~> 0.4.0) |
||||
|
rexml (>= 3.3.6, < 4.0) |
||||
|
zeitwerk (2.6.18) |
||||
|
|
||||
|
PLATFORMS |
||||
|
ruby |
||||
|
|
||||
|
DEPENDENCIES |
||||
|
cocoapods (~> 1.16) |
||||
|
cocoapods-spm (~> 0.1.15) |
||||
|
|
||||
|
BUNDLED WITH |
||||
|
1.17.2 |
||||
@ -1,9 +0,0 @@ |
|||||
import 'package:get/get.dart'; |
|
||||
import '../controllers/opus_test_controller.dart'; |
|
||||
|
|
||||
class OpusTestBinding extends Bindings { |
|
||||
@override |
|
||||
void dependencies() { |
|
||||
Get.lazyPut<OpusTestController>(() => OpusTestController()); |
|
||||
} |
|
||||
} |
|
||||
@ -1,404 +0,0 @@ |
|||||
import 'dart:async'; |
|
||||
import 'dart:io'; |
|
||||
import 'dart:typed_data'; |
|
||||
import 'package:file_picker/file_picker.dart'; |
|
||||
import 'package:flutter/foundation.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:get/get.dart'; |
|
||||
import 'package:path_provider/path_provider.dart'; |
|
||||
import 'package:just_audio/just_audio.dart'; |
|
||||
import 'package:jl_opus/jl_opus.dart'; |
|
||||
import 'package:permission_handler/permission_handler.dart'; |
|
||||
|
|
||||
class OpusTestController extends GetxController { |
|
||||
// 选中的文件 |
|
||||
final selectedFiles = <PlatformFile>[].obs; |
|
||||
|
|
||||
// 是否携带协议头 |
|
||||
final hasHeader = false.obs; |
|
||||
|
|
||||
// 数据包长度 |
|
||||
final packetLength = '40'.obs; |
|
||||
|
|
||||
// 采样率选项 |
|
||||
final sampleRateOptions = [8000, 16000, 24000, 32000, 44100, 48000]; |
|
||||
final selectedSampleRate = 16000.obs; |
|
||||
|
|
||||
// 通道数量 |
|
||||
final isMono = true.obs; |
|
||||
|
|
||||
// 播放状态 |
|
||||
final isPlaying = false.obs; |
|
||||
final isDecoding = false.obs; |
|
||||
final statusMessage = ''.obs; |
|
||||
|
|
||||
// 播放器 |
|
||||
final player = AudioPlayer(); |
|
||||
|
|
||||
// 临时文件路径 |
|
||||
String? tempPcmPath; |
|
||||
|
|
||||
// 杰理OPUS解码器 |
|
||||
late JlOpus jlOpus; |
|
||||
StreamSubscription? _eventSubscription; |
|
||||
|
|
||||
@override |
|
||||
void onInit() { |
|
||||
super.onInit(); |
|
||||
requestPermissions(); |
|
||||
_initOpusDecoder(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void onClose() { |
|
||||
player.dispose(); |
|
||||
_eventSubscription?.cancel(); |
|
||||
jlOpus.dispose(); |
|
||||
super.onClose(); |
|
||||
} |
|
||||
|
|
||||
// 初始化OPUS解码器 |
|
||||
Future<void> _initOpusDecoder() async { |
|
||||
jlOpus = JlOpus(); |
|
||||
|
|
||||
// 监听解码器事件 |
|
||||
_eventSubscription = jlOpus.eventStream.listen((event) { |
|
||||
switch (event.event) { |
|
||||
case 'onStart': |
|
||||
statusMessage.value = '开始${event.type == "file" ? "文件" : "流"}解码...'; |
|
||||
break; |
|
||||
case 'onComplete': |
|
||||
if (event.type == 'file') { |
|
||||
statusMessage.value = '解码完成: ${event.filePath}'; |
|
||||
} else { |
|
||||
statusMessage.value = '流解码完成'; |
|
||||
} |
|
||||
break; |
|
||||
case 'onError': |
|
||||
statusMessage.value = '解码错误: [${event.errorCode}] ${event.errorMessage}'; |
|
||||
isDecoding.value = false; |
|
||||
break; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 初始化OPUS解码器 |
|
||||
final initialized = await jlOpus.initOpusDecoder(); |
|
||||
if (!initialized) { |
|
||||
statusMessage.value = 'OPUS解码器初始化失败'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 请求必要权限 |
|
||||
Future<void> requestPermissions() async { |
|
||||
if (Platform.isAndroid) { |
|
||||
await Permission.storage.request(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 选择文件 |
|
||||
Future<void> pickFiles() async { |
|
||||
try { |
|
||||
final result = await FilePicker.platform.pickFiles( |
|
||||
type: FileType.custom, |
|
||||
allowedExtensions: ['opus'], |
|
||||
allowMultiple: false, |
|
||||
); |
|
||||
|
|
||||
if (result != null && result.files.isNotEmpty) { |
|
||||
selectedFiles.clear(); |
|
||||
selectedFiles.add(result.files.first); |
|
||||
statusMessage.value = '已选择文件:${result.files.first.name}'; |
|
||||
} |
|
||||
} catch (e) { |
|
||||
statusMessage.value = '选择文件失败: $e'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 切换通道模式 |
|
||||
void toggleChannel(bool isSingleChannel) { |
|
||||
isMono.value = isSingleChannel; |
|
||||
} |
|
||||
|
|
||||
// 设置采样率 |
|
||||
void setSampleRate(int rate) { |
|
||||
selectedSampleRate.value = rate; |
|
||||
} |
|
||||
|
|
||||
// 设置数据包长度 |
|
||||
void setPacketLength(String length) { |
|
||||
packetLength.value = length; |
|
||||
} |
|
||||
|
|
||||
// 切换是否携带协议头 |
|
||||
void toggleHeader(bool value) { |
|
||||
hasHeader.value = value; |
|
||||
} |
|
||||
|
|
||||
// 播放音频 |
|
||||
Future<void> playAudio() async { |
|
||||
if (isPlaying.value) { |
|
||||
await player.stop(); |
|
||||
isPlaying.value = false; |
|
||||
statusMessage.value = '已停止播放'; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (selectedFiles.isEmpty) { |
|
||||
statusMessage.value = '请先选择Opus文件'; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
isDecoding.value = true; |
|
||||
statusMessage.value = '正在解码...'; |
|
||||
|
|
||||
// 使用杰理OPUS库进行解码 |
|
||||
final inPath = selectedFiles.first.path!; |
|
||||
|
|
||||
// 创建WAV输出文件路径 |
|
||||
final tempDir = await getTemporaryDirectory(); |
|
||||
final wavFileName = 'decoded_opus_${DateTime.now().millisecondsSinceEpoch}.wav'; |
|
||||
final outPath = '${tempDir.path}/$wavFileName'; |
|
||||
|
|
||||
// 创建解码参数 |
|
||||
final option = OpusOption( |
|
||||
hasHeader: hasHeader.value, |
|
||||
channel: isMono.value ? 1 : 2, |
|
||||
sampleRate: selectedSampleRate.value, |
|
||||
packetSize: int.tryParse(packetLength.value) ?? 40, |
|
||||
); |
|
||||
|
|
||||
// 解码文件 |
|
||||
final pcmPath = await jlOpus.decodeOpusFile(inPath, outPath, option); |
|
||||
if (pcmPath == null) { |
|
||||
statusMessage.value = '解码失败'; |
|
||||
isDecoding.value = false; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
tempPcmPath = pcmPath; |
|
||||
|
|
||||
// 将PCM文件转换为WAV文件 |
|
||||
final wavFile = await _convertPcmToWav( |
|
||||
pcmPath, |
|
||||
'${tempDir.path}/$wavFileName', |
|
||||
selectedSampleRate.value, |
|
||||
isMono.value ? 1 : 2 |
|
||||
); |
|
||||
|
|
||||
statusMessage.value = '解码完成,准备播放'; |
|
||||
|
|
||||
// 播放WAV文件 |
|
||||
await player.setFilePath(wavFile.path); |
|
||||
await player.play(); |
|
||||
isPlaying.value = true; |
|
||||
statusMessage.value = '正在播放解码后的音频'; |
|
||||
|
|
||||
// 监听播放完成 |
|
||||
player.playerStateStream.listen((state) { |
|
||||
if (state.processingState == ProcessingState.completed) { |
|
||||
isPlaying.value = false; |
|
||||
statusMessage.value = '播放完成'; |
|
||||
} |
|
||||
}); |
|
||||
} catch (e) { |
|
||||
statusMessage.value = '解码或播放失败: $e'; |
|
||||
} finally { |
|
||||
isDecoding.value = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 将PCM转换为WAV格式 |
|
||||
Future<File> _convertPcmToWav(String pcmPath, String wavPath, int sampleRate, int channels) async { |
|
||||
final pcmFile = File(pcmPath); |
|
||||
final pcmData = await pcmFile.readAsBytes(); |
|
||||
|
|
||||
// 创建WAV文件 (44字节头部 + PCM数据) |
|
||||
final wavSize = 44 + pcmData.length; |
|
||||
final wavData = ByteData(wavSize); |
|
||||
|
|
||||
// 写入WAV头部 |
|
||||
_writeWavHeader(wavData, pcmData.length, sampleRate, channels); |
|
||||
|
|
||||
// 写入PCM数据 |
|
||||
for (int i = 0; i < pcmData.length; i++) { |
|
||||
wavData.setUint8(44 + i, pcmData[i]); |
|
||||
} |
|
||||
|
|
||||
// 保存为WAV文件 |
|
||||
final wavFile = File(wavPath); |
|
||||
await wavFile.writeAsBytes(wavData.buffer.asUint8List()); |
|
||||
|
|
||||
return wavFile; |
|
||||
} |
|
||||
|
|
||||
// 写入WAV头部到ByteData |
|
||||
void _writeWavHeader(ByteData data, int pcmLength, int sampleRate, int channels) { |
|
||||
final bytesPerSample = 2; // 16位PCM |
|
||||
final byteRate = sampleRate * channels * bytesPerSample; |
|
||||
final blockAlign = channels * bytesPerSample; |
|
||||
|
|
||||
// "RIFF"标识 |
|
||||
data.setUint8(0, 0x52); // 'R' |
|
||||
data.setUint8(1, 0x49); // 'I' |
|
||||
data.setUint8(2, 0x46); // 'F' |
|
||||
data.setUint8(3, 0x46); // 'F' |
|
||||
|
|
||||
// 文件大小 (减去8个字节的RIFF头部和大小字段) |
|
||||
data.setUint32(4, 36 + pcmLength, Endian.little); |
|
||||
|
|
||||
// "WAVE"标识 |
|
||||
data.setUint8(8, 0x57); // 'W' |
|
||||
data.setUint8(9, 0x41); // 'A' |
|
||||
data.setUint8(10, 0x56); // 'V' |
|
||||
data.setUint8(11, 0x45); // 'E' |
|
||||
|
|
||||
// "fmt "子块 |
|
||||
data.setUint8(12, 0x66); // 'f' |
|
||||
data.setUint8(13, 0x6D); // 'm' |
|
||||
data.setUint8(14, 0x74); // 't' |
|
||||
data.setUint8(15, 0x20); // ' ' |
|
||||
|
|
||||
// 子块大小 (16 for PCM) |
|
||||
data.setUint32(16, 16, Endian.little); |
|
||||
|
|
||||
// 音频格式 (1 表示PCM) |
|
||||
data.setUint16(20, 1, Endian.little); |
|
||||
|
|
||||
// 通道数量 |
|
||||
data.setUint16(22, channels, Endian.little); |
|
||||
|
|
||||
// 采样率 |
|
||||
data.setUint32(24, sampleRate, Endian.little); |
|
||||
|
|
||||
// 字节率 = 采样率 * 通道数 * 每样本字节数 |
|
||||
data.setUint32(28, byteRate, Endian.little); |
|
||||
|
|
||||
// 块对齐 = 通道数 * 每样本字节数 |
|
||||
data.setUint16(32, blockAlign, Endian.little); |
|
||||
|
|
||||
// 每个样本位数 |
|
||||
data.setUint16(34, bytesPerSample * 8, Endian.little); |
|
||||
|
|
||||
// "data"子块 |
|
||||
data.setUint8(36, 0x64); // 'd' |
|
||||
data.setUint8(37, 0x61); // 'a' |
|
||||
data.setUint8(38, 0x74); // 't' |
|
||||
data.setUint8(39, 0x61); // 'a' |
|
||||
|
|
||||
// 数据大小 |
|
||||
data.setUint32(40, pcmLength, Endian.little); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// PCM音频源 |
|
||||
class PCMAudioSource extends StreamAudioSource { |
|
||||
final Uint8List pcmData; |
|
||||
final int sampleRate; |
|
||||
final int channels; |
|
||||
|
|
||||
PCMAudioSource({ |
|
||||
required this.pcmData, |
|
||||
this.sampleRate = 16000, |
|
||||
this.channels = 1, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Future<StreamAudioResponse> request([int? start, int? end]) async { |
|
||||
start = start ?? 0; |
|
||||
end = end ?? pcmData.length; |
|
||||
|
|
||||
// 转换PCM到WAV格式 |
|
||||
final wavHeader = _createWavHeader(pcmData.length, sampleRate, channels); |
|
||||
final wavData = Uint8List(wavHeader.length + pcmData.length); |
|
||||
wavData.setRange(0, wavHeader.length, wavHeader); |
|
||||
wavData.setRange(wavHeader.length, wavHeader.length + pcmData.length, pcmData); |
|
||||
|
|
||||
// 调整请求范围来适应WAV头部 |
|
||||
int adjustedStart = start; |
|
||||
int adjustedEnd = end; |
|
||||
|
|
||||
if (start < wavHeader.length) { |
|
||||
adjustedStart = start; |
|
||||
} else { |
|
||||
adjustedStart = start + wavHeader.length; |
|
||||
} |
|
||||
|
|
||||
if (end <= wavHeader.length) { |
|
||||
adjustedEnd = end; |
|
||||
} else { |
|
||||
adjustedEnd = end + wavHeader.length; |
|
||||
adjustedEnd = adjustedEnd > wavData.length ? wavData.length : adjustedEnd; |
|
||||
} |
|
||||
|
|
||||
return StreamAudioResponse( |
|
||||
sourceLength: wavData.length, |
|
||||
contentLength: adjustedEnd - adjustedStart, |
|
||||
offset: adjustedStart, |
|
||||
stream: Stream.value(wavData.sublist(adjustedStart, adjustedEnd)), |
|
||||
contentType: 'audio/wav', |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
// 创建WAV头部 |
|
||||
Uint8List _createWavHeader(int dataLength, int sampleRate, int channels) { |
|
||||
final bytesPerSample = 2; // 16位PCM |
|
||||
final byteRate = sampleRate * channels * bytesPerSample; |
|
||||
final blockAlign = channels * bytesPerSample; |
|
||||
|
|
||||
final header = ByteData(44); // WAV头部固定44字节 |
|
||||
|
|
||||
// "RIFF"标识 |
|
||||
header.setUint8(0, 0x52); // 'R' |
|
||||
header.setUint8(1, 0x49); // 'I' |
|
||||
header.setUint8(2, 0x46); // 'F' |
|
||||
header.setUint8(3, 0x46); // 'F' |
|
||||
|
|
||||
// 文件大小 (减去8个字节的RIFF头部和大小字段) |
|
||||
header.setUint32(4, 36 + dataLength, Endian.little); |
|
||||
|
|
||||
// "WAVE"标识 |
|
||||
header.setUint8(8, 0x57); // 'W' |
|
||||
header.setUint8(9, 0x41); // 'A' |
|
||||
header.setUint8(10, 0x56); // 'V' |
|
||||
header.setUint8(11, 0x45); // 'E' |
|
||||
|
|
||||
// "fmt "子块 |
|
||||
header.setUint8(12, 0x66); // 'f' |
|
||||
header.setUint8(13, 0x6D); // 'm' |
|
||||
header.setUint8(14, 0x74); // 't' |
|
||||
header.setUint8(15, 0x20); // ' ' |
|
||||
|
|
||||
// 子块大小 (16 for PCM) |
|
||||
header.setUint32(16, 16, Endian.little); |
|
||||
|
|
||||
// 音频格式 (1 表示PCM) |
|
||||
header.setUint16(20, 1, Endian.little); |
|
||||
|
|
||||
// 通道数量 |
|
||||
header.setUint16(22, channels, Endian.little); |
|
||||
|
|
||||
// 采样率 |
|
||||
header.setUint32(24, sampleRate, Endian.little); |
|
||||
|
|
||||
// 字节率 = 采样率 * 通道数 * 每样本字节数 |
|
||||
header.setUint32(28, byteRate, Endian.little); |
|
||||
|
|
||||
// 块对齐 = 通道数 * 每样本字节数 |
|
||||
header.setUint16(32, blockAlign, Endian.little); |
|
||||
|
|
||||
// 每个样本位数 |
|
||||
header.setUint16(34, bytesPerSample * 8, Endian.little); |
|
||||
|
|
||||
// "data"子块 |
|
||||
header.setUint8(36, 0x64); // 'd' |
|
||||
header.setUint8(37, 0x61); // 'a' |
|
||||
header.setUint8(38, 0x74); // 't' |
|
||||
header.setUint8(39, 0x61); // 'a' |
|
||||
|
|
||||
// 数据大小 |
|
||||
header.setUint32(40, dataLength, Endian.little); |
|
||||
|
|
||||
return header.buffer.asUint8List(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,445 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|
||||
import 'package:get/get.dart'; |
|
||||
import '../controllers/opus_test_controller.dart'; |
|
||||
|
|
||||
class OpusTestView extends GetView<OpusTestController> { |
|
||||
const OpusTestView({Key? key}) : super(key: key); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
// 获取主题模式 |
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|
||||
|
|
||||
return DefaultTabController( |
|
||||
length: 2, |
|
||||
child: Scaffold( |
|
||||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|
||||
appBar: AppBar( |
|
||||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|
||||
elevation: 0.5, |
|
||||
title: Text( |
|
||||
'OPUS解码', |
|
||||
style: TextStyle( |
|
||||
fontSize: 17.sp, |
|
||||
fontWeight: FontWeight.w600, |
|
||||
color: isDarkMode ? Colors.white : Colors.black, |
|
||||
), |
|
||||
), |
|
||||
centerTitle: true, |
|
||||
bottom: TabBar( |
|
||||
indicatorColor: Colors.blue, |
|
||||
indicatorWeight: 3, |
|
||||
labelColor: isDarkMode ? Colors.white : Colors.black, |
|
||||
unselectedLabelColor: isDarkMode ? Colors.grey : Colors.grey, |
|
||||
tabs: const [ |
|
||||
Tab(text: "解码"), |
|
||||
Tab(text: "输出"), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
body: TabBarView( |
|
||||
children: [ |
|
||||
_buildDecodeTab(isDarkMode), |
|
||||
_buildEncodeTab(isDarkMode), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
// 解码标签页 |
|
||||
Widget _buildDecodeTab(bool isDarkMode) { |
|
||||
return ListView( |
|
||||
padding: EdgeInsets.all(16.w), |
|
||||
children: [ |
|
||||
// 文件选择部分 |
|
||||
_buildSection( |
|
||||
title: '选择文件', |
|
||||
isDarkMode: isDarkMode, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Obx(() => controller.selectedFiles.isEmpty |
|
||||
? const SizedBox.shrink() |
|
||||
: ListView.builder( |
|
||||
shrinkWrap: true, |
|
||||
physics: const NeverScrollableScrollPhysics(), |
|
||||
itemCount: controller.selectedFiles.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
final file = controller.selectedFiles[index]; |
|
||||
return ListTile( |
|
||||
title: Text( |
|
||||
file.name, |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
'${(file.size / 1024).toStringAsFixed(2)} Kb', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white38 : Colors.black54, |
|
||||
fontSize: 12.sp, |
|
||||
), |
|
||||
), |
|
||||
trailing: Radio<int>( |
|
||||
value: index, |
|
||||
groupValue: 0, |
|
||||
onChanged: (value) {}, |
|
||||
activeColor: Colors.blue, |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
)), |
|
||||
Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.end, |
|
||||
children: [ |
|
||||
IconButton( |
|
||||
icon: Icon( |
|
||||
Icons.refresh, |
|
||||
color: isDarkMode ? Colors.blue[300] : Colors.blue, |
|
||||
), |
|
||||
onPressed: () { |
|
||||
controller.selectedFiles.clear(); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
SizedBox(height: 16.h), |
|
||||
|
|
||||
// 解码参数部分 |
|
||||
_buildSection( |
|
||||
title: '解码参数', |
|
||||
isDarkMode: isDarkMode, |
|
||||
child: Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.start, |
|
||||
children: [ |
|
||||
Row( |
|
||||
children: [ |
|
||||
Obx(() => Checkbox( |
|
||||
value: controller.hasHeader.value, |
|
||||
onChanged: (value) => controller.toggleHeader(value!), |
|
||||
activeColor: Colors.blue, |
|
||||
)), |
|
||||
Text( |
|
||||
'是否携带协议头', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
// 数据包长度 |
|
||||
Row( |
|
||||
children: [ |
|
||||
Text( |
|
||||
'数据包长度', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
SizedBox(width: 16.w), |
|
||||
Expanded( |
|
||||
child: Obx(() => TextField( |
|
||||
controller: TextEditingController( |
|
||||
text: controller.packetLength.value), |
|
||||
onChanged: (value) => controller.setPacketLength(value), |
|
||||
keyboardType: TextInputType.number, |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white : Colors.black, |
|
||||
), |
|
||||
decoration: InputDecoration( |
|
||||
isDense: true, |
|
||||
suffix: Text( |
|
||||
'Byte', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode |
|
||||
? Colors.white60 |
|
||||
: Colors.black54, |
|
||||
fontSize: 12.sp, |
|
||||
), |
|
||||
), |
|
||||
enabledBorder: UnderlineInputBorder( |
|
||||
borderSide: BorderSide( |
|
||||
color: isDarkMode |
|
||||
? Colors.white30 |
|
||||
: Colors.black26, |
|
||||
), |
|
||||
), |
|
||||
focusedBorder: UnderlineInputBorder( |
|
||||
borderSide: BorderSide( |
|
||||
color: Colors.blue, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
)), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
SizedBox(height: 20.h), |
|
||||
|
|
||||
// 采样率 |
|
||||
Row( |
|
||||
children: [ |
|
||||
Text( |
|
||||
'采样率', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
SizedBox(width: 16.w), |
|
||||
Expanded( |
|
||||
child: Obx(() => DropdownButton<int>( |
|
||||
value: controller.selectedSampleRate.value, |
|
||||
isExpanded: true, |
|
||||
dropdownColor: |
|
||||
isDarkMode ? Colors.grey[800] : Colors.white, |
|
||||
items: controller.sampleRateOptions.map((rate) { |
|
||||
return DropdownMenuItem<int>( |
|
||||
value: rate, |
|
||||
child: Text( |
|
||||
rate.toString(), |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode |
|
||||
? Colors.white |
|
||||
: Colors.black87, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
}).toList(), |
|
||||
onChanged: (value) => |
|
||||
controller.setSampleRate(value!), |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white : Colors.black, |
|
||||
), |
|
||||
)), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
SizedBox(height: 20.h), |
|
||||
|
|
||||
// 通道数量 |
|
||||
Text( |
|
||||
'通道数量', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
Row( |
|
||||
children: [ |
|
||||
Expanded( |
|
||||
child: Row( |
|
||||
children: [ |
|
||||
Obx(() => Radio<bool>( |
|
||||
value: true, |
|
||||
groupValue: controller.isMono.value, |
|
||||
onChanged: (value) => |
|
||||
controller.toggleChannel(value!), |
|
||||
activeColor: Colors.blue, |
|
||||
)), |
|
||||
Text( |
|
||||
'单通道', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
Expanded( |
|
||||
child: Row( |
|
||||
children: [ |
|
||||
Obx(() => Radio<bool>( |
|
||||
value: false, |
|
||||
groupValue: controller.isMono.value, |
|
||||
onChanged: (value) => |
|
||||
controller.toggleChannel(value!), |
|
||||
activeColor: Colors.blue, |
|
||||
)), |
|
||||
Text( |
|
||||
'双通道', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white70 : Colors.black87, |
|
||||
fontSize: 14.sp, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
|
|
||||
SizedBox(height: 16.h), |
|
||||
|
|
||||
// 状态消息 |
|
||||
Obx(() => controller.statusMessage.value.isNotEmpty |
|
||||
? Container( |
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 16.w), |
|
||||
margin: EdgeInsets.only(bottom: 16.h), |
|
||||
decoration: BoxDecoration( |
|
||||
color: isDarkMode |
|
||||
? Colors.blue.withOpacity(0.2) |
|
||||
: Colors.blue.withOpacity(0.1), |
|
||||
borderRadius: BorderRadius.circular(4.r), |
|
||||
border: Border.all( |
|
||||
color: isDarkMode |
|
||||
? Colors.blue.withOpacity(0.3) |
|
||||
: Colors.blue.withOpacity(0.2), |
|
||||
width: 1, |
|
||||
), |
|
||||
), |
|
||||
child: Text( |
|
||||
controller.statusMessage.value, |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.blue[300] : Colors.blue[700], |
|
||||
fontSize: 12.sp, |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: const SizedBox.shrink()), |
|
||||
|
|
||||
// 播放按钮 |
|
||||
Container( |
|
||||
margin: EdgeInsets.symmetric(vertical: 8.h), |
|
||||
child: ElevatedButton.icon( |
|
||||
icon: Obx(() => Icon( |
|
||||
controller.isPlaying.value |
|
||||
? Icons.stop |
|
||||
: Icons.play_arrow, |
|
||||
color: Colors.white, |
|
||||
)), |
|
||||
label: Obx(() => Text( |
|
||||
controller.isPlaying.value ? '停止播放' : '播放音频', |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: 16.sp, |
|
||||
), |
|
||||
)), |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
backgroundColor: Colors.blue, |
|
||||
padding: EdgeInsets.symmetric(vertical: 16.h), |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.circular(8.r), |
|
||||
), |
|
||||
), |
|
||||
onPressed: controller.isDecoding.value |
|
||||
? null |
|
||||
: () { |
|
||||
controller.playAudio(); |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
|
|
||||
// 解码按钮 |
|
||||
Container( |
|
||||
margin: EdgeInsets.symmetric(vertical: 8.h), |
|
||||
child: Obx(() => ElevatedButton( |
|
||||
onPressed: controller.selectedFiles.isEmpty || |
|
||||
controller.isDecoding.value |
|
||||
? null |
|
||||
: () { |
|
||||
controller.pickFiles(); |
|
||||
}, |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
backgroundColor: Colors.blue, |
|
||||
padding: EdgeInsets.symmetric(vertical: 16.h), |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.circular(8.r), |
|
||||
), |
|
||||
), |
|
||||
child: Text( |
|
||||
'开始解码', |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: 16.sp, |
|
||||
), |
|
||||
), |
|
||||
)), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
// 编码标签页(暂未实现) |
|
||||
Widget _buildEncodeTab(bool isDarkMode) { |
|
||||
return Center( |
|
||||
child: Text( |
|
||||
'编码功能尚未实现', |
|
||||
style: TextStyle( |
|
||||
color: isDarkMode ? Colors.white54 : Colors.black54, |
|
||||
fontSize: 16.sp, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
// 构建带标题的区段 |
|
||||
Widget _buildSection({ |
|
||||
required String title, |
|
||||
required Widget child, |
|
||||
required bool isDarkMode, |
|
||||
}) { |
|
||||
return Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.start, |
|
||||
children: [ |
|
||||
Row( |
|
||||
children: [ |
|
||||
Text( |
|
||||
title, |
|
||||
style: TextStyle( |
|
||||
fontSize: 16.sp, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: isDarkMode ? Colors.white : Colors.black87, |
|
||||
), |
|
||||
), |
|
||||
SizedBox(width: 8.w), |
|
||||
Expanded( |
|
||||
child: Container( |
|
||||
height: 1, |
|
||||
color: isDarkMode ? Colors.white24 : Colors.black12, |
|
||||
), |
|
||||
), |
|
||||
if (title == '选择文件') |
|
||||
IconButton( |
|
||||
icon: Icon( |
|
||||
Icons.add_circle_outline, |
|
||||
color: isDarkMode ? Colors.blue[300] : Colors.blue, |
|
||||
), |
|
||||
onPressed: () { |
|
||||
controller.pickFiles(); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
Container( |
|
||||
padding: EdgeInsets.all(12.w), |
|
||||
decoration: BoxDecoration( |
|
||||
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, |
|
||||
borderRadius: BorderRadius.circular(8.r), |
|
||||
boxShadow: [ |
|
||||
BoxShadow( |
|
||||
color: isDarkMode |
|
||||
? Colors.black.withOpacity(0.2) |
|
||||
: Colors.black.withOpacity(0.03), |
|
||||
blurRadius: 4, |
|
||||
offset: Offset(0, 2), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
child: child, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,4 +1,4 @@ |
|||||
#import <Flutter/Flutter.h> |
#import <Flutter/Flutter.h> |
||||
|
|
||||
@interface AgentServicePlugin : NSObject<FlutterPlugin> |
@interface AgentServicePlugin : NSObject<FlutterPlugin> |
||||
@end |
@end |
||||
@ -1,227 +1,160 @@ |
|||||
import Flutter |
import Flutter |
||||
import UIKit |
import UIKit |
||||
import AVFoundation |
import Foundation |
||||
|
import os.log |
||||
|
|
||||
/// 代理服务插件,负责连接Flutter端与原生服务 |
/// 代理服务Flutter插件,负责将Flutter调用桥接到原生实现 |
||||
public class AgentServicePlugin: NSObject, FlutterPlugin { |
public class AgentServicePlugin: NSObject, FlutterPlugin { |
||||
private let TAG = "AgentServicePlugin" |
|
||||
private var eventSink: FlutterEventSink? |
|
||||
|
|
||||
// MARK: - 插件注册 |
|
||||
|
|
||||
/// 插件注册方法 |
/// 插件注册方法,Flutter引擎调用 |
||||
public static func register(with registrar: FlutterPluginRegistrar) { |
public static func register(with registrar: FlutterPluginRegistrar) { |
||||
let methodChannel = FlutterMethodChannel(name: "com.yunqiinnovation.agent_service", binaryMessenger: registrar.messenger()) |
let channel = FlutterMethodChannel(name: "com.yunqiinnovation.agent_service", binaryMessenger: registrar.messenger()) |
||||
let eventChannel = FlutterEventChannel(name: "com.yunqiinnovation.agent_service/events", binaryMessenger: registrar.messenger()) |
let eventChannel = FlutterEventChannel(name: "com.yunqiinnovation.agent_service/events", binaryMessenger: registrar.messenger()) |
||||
|
|
||||
let instance = AgentServicePlugin() |
let instance = AgentServicePlugin() |
||||
|
|
||||
registrar.addMethodCallDelegate(instance, channel: methodChannel) |
registrar.addMethodCallDelegate(instance, channel: channel) |
||||
eventChannel.setStreamHandler(instance) |
eventChannel.setStreamHandler(instance.eventHandler) |
||||
|
|
||||
// 添加监听器 |
|
||||
AgentServiceImpl.shared.addListener(instance) |
|
||||
|
|
||||
NSLog("\(instance.TAG): 插件已加载") |
|
||||
} |
} |
||||
|
|
||||
// MARK: - 方法处理 |
// MARK: - 属性 |
||||
|
|
||||
/// 处理来自Flutter的方法调用 |
private let impl = AgentServiceImpl.shared |
||||
|
private lazy var eventHandler = AgentServiceEventHandler(impl: impl) |
||||
|
|
||||
|
// MARK: - FlutterPlugin 实现 |
||||
|
|
||||
|
/// 处理Flutter发来的方法调用 |
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
||||
NSLog("\(TAG): 收到方法调用 - \(call.method)") |
|
||||
|
|
||||
switch call.method { |
switch call.method { |
||||
case "initialize": |
|
||||
handleInitialize(call, result: result) |
|
||||
case "startAgentService": |
case "startAgentService": |
||||
handleStartAgentService(call, result: result) |
guard let arguments = call.arguments as? [String: Any], |
||||
|
let config = arguments["config"] as? [String: Any] else { |
||||
|
result(FlutterError(code: "INVALID_ARGUMENTS", message: "配置参数不能为空", details: nil)) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let success = impl.initialize(config: config) |
||||
|
result(success) |
||||
|
|
||||
case "stopAgentService": |
case "stopAgentService": |
||||
handleStopAgentService(result: result) |
let success = impl.dispose() |
||||
|
result(success) |
||||
|
|
||||
case "startConversation": |
case "startConversation": |
||||
handleStartConversation(result: result) |
impl.startRecognition() |
||||
|
result(true) |
||||
|
|
||||
case "stopConversation": |
case "stopConversation": |
||||
handleStopConversation(result: result) |
impl.stopRecognition() |
||||
|
result(true) |
||||
|
|
||||
case "processTextInput": |
case "processTextInput": |
||||
handleProcessTextInput(call, result: result) |
guard let arguments = call.arguments as? [String: Any], |
||||
|
let text = arguments["text"] as? String else { |
||||
|
result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let speakResponse = arguments["speakResponse"] as? Bool ?? false |
||||
|
let success = impl.processTextInput(text, speakResponse: speakResponse) |
||||
|
result(success) |
||||
|
|
||||
|
case "processImageInput": |
||||
|
guard let arguments = call.arguments as? [String: Any], |
||||
|
let imagePath = arguments["imagePath"] as? String else { |
||||
|
result(FlutterError(code: "INVALID_ARGUMENTS", message: "图片路径不能为空", details: nil)) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let text = arguments["text"] as? String ?? "" |
||||
|
let speakResponse = arguments["speakResponse"] as? Bool ?? false |
||||
|
let success = impl.processImageInput(imagePath: imagePath, text: text, speakResponse: speakResponse) |
||||
|
result(success) |
||||
|
|
||||
case "speakText": |
case "speakText": |
||||
handleSpeakText(call, result: result) |
guard let arguments = call.arguments as? [String: Any], |
||||
|
let text = arguments["text"] as? String else { |
||||
|
result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let success = impl.speakText(text) |
||||
|
result(success) |
||||
|
|
||||
case "stopTts": |
case "stopTts": |
||||
handleStopTts(result: result) |
let success = impl.stopTts() |
||||
|
result(success) |
||||
|
|
||||
case "clearChatHistory": |
case "clearChatHistory": |
||||
handleClearChatHistory(result: result) |
let success = impl.clearChatHistory() |
||||
|
result(success) |
||||
|
|
||||
case "interruptCurrentResponse": |
case "interruptCurrentResponse": |
||||
handleInterruptCurrentResponse(result: result) |
let success = impl.interruptCurrentResponse() |
||||
case "processImageInput": |
result(success) |
||||
handleProcessImageInput(call, result: result) |
|
||||
case "dispose": |
case "dispose": |
||||
handleDispose(result: result) |
let success = impl.dispose() |
||||
|
result(success) |
||||
|
|
||||
default: |
default: |
||||
result(FlutterMethodNotImplemented) |
result(FlutterMethodNotImplemented) |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
/// 代理服务事件处理器,负责将原生事件传递给Flutter |
||||
|
class AgentServiceEventHandler: NSObject, FlutterStreamHandler { |
||||
|
private let impl: AgentServiceImpl |
||||
|
private var eventSink: FlutterEventSink? |
||||
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.agent_service", category: "AgentServiceEventHandler") |
||||
|
|
||||
// MARK: - 方法实现 |
init(impl: AgentServiceImpl) { |
||||
|
self.impl = impl |
||||
private func handleInitialize(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
super.init() |
||||
guard let args = call.arguments as? [String: Any], |
|
||||
let config = args["config"] as? [String: Any] else { |
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "参数无效", details: nil)) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 初始化AgentServiceImpl |
|
||||
let success = AgentServiceImpl.shared.initialize(config: config) |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleStartAgentService(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|
||||
guard let args = call.arguments as? [String: Any], |
|
||||
let config = args["config"] as? [String: Any] else { |
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "参数无效", details: nil)) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
NSLog("\(TAG): 启动AgentService,配置参数: \(config)") |
|
||||
|
|
||||
// 初始化AgentServiceImpl |
|
||||
let success = AgentServiceImpl.shared.initialize(config: config) |
|
||||
|
|
||||
if success { |
|
||||
result(true) |
|
||||
} else { |
|
||||
result(FlutterError(code: "SERVICE_START_ERROR", message: "AgentService启动失败", details: nil)) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private func handleStopAgentService(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 停止AgentService") |
|
||||
let success = AgentServiceImpl.shared.dispose() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleStartConversation(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 开始对话") |
|
||||
let success = AgentServiceImpl.shared.startRecognition() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleStopConversation(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 停止对话") |
|
||||
let success = AgentServiceImpl.shared.stopRecognition() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleProcessTextInput(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|
||||
guard let args = call.arguments as? [String: Any], |
|
||||
let text = args["text"] as? String else { |
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) |
|
||||
return |
|
||||
} |
|
||||
let speakResponse = args["speakResponse"] as? Bool ?? false |
|
||||
|
|
||||
NSLog("\(TAG): 处理文本输入 - \(text)") |
|
||||
let success = AgentServiceImpl.shared.processTextInput(text, speakResponse: speakResponse) |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleSpeakText(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|
||||
guard let args = call.arguments as? [String: Any], |
|
||||
let text = args["text"] as? String else { |
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
NSLog("\(TAG): 语音合成 - \(text)") |
|
||||
let success = AgentServiceImpl.shared.speakText(text) |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleStopTts(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 停止语音合成") |
|
||||
let success = AgentServiceImpl.shared.stopTts() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleClearChatHistory(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 清除聊天历史") |
|
||||
let success = AgentServiceImpl.shared.clearChatHistory() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleInterruptCurrentResponse(result: @escaping FlutterResult) { |
|
||||
NSLog("\(TAG): 中断当前响应") |
|
||||
let success = AgentServiceImpl.shared.interruptCurrentResponse() |
|
||||
result(success) |
|
||||
} |
|
||||
|
|
||||
private func handleProcessImageInput(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|
||||
guard let args = call.arguments as? [String: Any], |
|
||||
let imagePath = args["imagePath"] as? String else { |
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "图片路径不能为空", details: nil)) |
|
||||
return |
|
||||
} |
|
||||
let text = args["text"] as? String ?? "" |
|
||||
let speakResponse = args["speakResponse"] as? Bool ?? false |
|
||||
|
|
||||
NSLog("\(TAG): 处理图片输入 - \(imagePath)") |
|
||||
let success = AgentServiceImpl.shared.processImageInput(imagePath: imagePath, text: text, speakResponse: speakResponse) |
|
||||
result(success) |
|
||||
} |
} |
||||
|
|
||||
private func handleDispose(result: @escaping FlutterResult) { |
/// 监听开始时调用 |
||||
NSLog("\(TAG): 释放资源") |
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { |
||||
|
self.eventSink = events |
||||
// 移除监听器并释放资源 |
|
||||
AgentServiceImpl.shared.removeListener(self) |
|
||||
let success = AgentServiceImpl.shared.dispose() |
|
||||
|
|
||||
result(success) |
// 添加实现的事件监听器 |
||||
|
impl.addListener(self) |
||||
|
return nil |
||||
} |
} |
||||
|
|
||||
// MARK: - 实例析构 |
/// 监听取消时调用 |
||||
|
func onCancel(withArguments arguments: Any?) -> FlutterError? { |
||||
deinit { |
impl.removeListener(self) |
||||
// 确保在实例销毁时移除监听器 |
self.eventSink = nil |
||||
AgentServiceImpl.shared.removeListener(self) |
return nil |
||||
NSLog("\(TAG): 插件已卸载") |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// MARK: - AgentServiceListener 实现 |
// 扩展事件处理器以实现AgentServiceListener接口 |
||||
extension AgentServicePlugin: AgentServiceListener { |
extension AgentServiceEventHandler: AgentServiceListener { |
||||
public func onEvent(eventName: String, data: [String: Any]) { |
func onEvent(eventName: String, data: [String: Any]) { |
||||
guard let eventSink = eventSink else { return } |
// 确保在主线程上调用 |
||||
|
DispatchQueue.main.async { [weak self] in |
||||
// 将事件转换为JSON字符串 |
guard let self = self else { return } |
||||
let eventData: [String: Any] = [ |
|
||||
"event": eventName, |
// 将事件数据格式化为与Android版本一致的格式 |
||||
"data": data |
let eventData: [String: Any] = [ |
||||
] |
"event": eventName, |
||||
|
"data": data |
||||
do { |
] |
||||
let jsonData = try JSONSerialization.data(withJSONObject: eventData, options: []) |
|
||||
if let jsonString = String(data: jsonData, encoding: .utf8) { |
// 转换为JSON字符串发送,与Android版本保持一致 |
||||
// 确保在主线程发送事件到Flutter |
do { |
||||
DispatchQueue.main.async { |
let jsonData = try JSONSerialization.data(withJSONObject: eventData, options: []) |
||||
eventSink(jsonString) |
if let jsonString = String(data: jsonData, encoding: .utf8) { |
||||
|
self.eventSink?(jsonString) |
||||
|
} else { |
||||
|
os_log("无法创建JSON字符串", log: self.logger, type: .error) |
||||
} |
} |
||||
|
} catch { |
||||
|
os_log("JSON序列化失败: %{public}@", log: self.logger, type: .error, error.localizedDescription) |
||||
} |
} |
||||
} catch { |
|
||||
NSLog("\(TAG): 错误 - 事件数据序列化失败: \(error.localizedDescription)") |
|
||||
} |
} |
||||
} |
} |
||||
} |
|
||||
|
|
||||
// MARK: - FlutterStreamHandler 实现 |
|
||||
extension AgentServicePlugin: FlutterStreamHandler { |
|
||||
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { |
|
||||
self.eventSink = events |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? { |
|
||||
self.eventSink = nil |
|
||||
return nil |
|
||||
} |
|
||||
} |
} |
||||
@ -0,0 +1,240 @@ |
|||||
|
import AVFoundation |
||||
|
import os.log |
||||
|
|
||||
|
public enum AudioIntent { |
||||
|
case voice |
||||
|
case playback |
||||
|
} |
||||
|
|
||||
|
public class AudioSessionHub { |
||||
|
|
||||
|
public static let shared = AudioSessionHub() |
||||
|
|
||||
|
private let logger = OSLog(subsystem: "com.yunqiinnovation.agent_service", category: "AudioSessionHub") |
||||
|
private var currentIntent: AudioIntent? |
||||
|
private let audioSession = AVAudioSession.sharedInstance() |
||||
|
private var isInterrupted = false |
||||
|
|
||||
|
private init() { |
||||
|
setupNotifications() |
||||
|
} |
||||
|
|
||||
|
public func begin(_ intent: AudioIntent) { |
||||
|
if intent == currentIntent && audioSession.isOtherAudioPlaying == false { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
currentIntent = intent |
||||
|
|
||||
|
do { |
||||
|
switch intent { |
||||
|
|
||||
|
case .voice: |
||||
|
try audioSession.setCategory(.playAndRecord, |
||||
|
mode: .voiceChat, |
||||
|
options: [.allowBluetooth, .mixWithOthers]) |
||||
|
|
||||
|
// 如果有蓝牙HFP设备,强制设置为首选输入 |
||||
|
if let bluetoothInput = audioSession.availableInputs?.first(where: { $0.portType == .bluetoothHFP }) { |
||||
|
try audioSession.setPreferredInput(bluetoothInput) |
||||
|
os_log("已设置蓝牙HFP为首选输入: %{public}@", log: logger, type: .info, bluetoothInput.portName) |
||||
|
} |
||||
|
|
||||
|
case .playback: |
||||
|
try audioSession.setCategory(.playback, |
||||
|
mode: .default, |
||||
|
options: [.allowBluetooth, .mixWithOthers]) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
try audioSession.setActive(true, options: .notifyOthersOnDeactivation) |
||||
|
|
||||
|
os_log("音频会话已设置为:%{public}@", log: logger, type: .debug, String(describing: intent)) |
||||
|
|
||||
|
// 输出当前音频路由信息 |
||||
|
let hasBluetoothDevice = hasBluetoothAudioDevice() |
||||
|
let hasBluetoothMic = hasBluetoothMicrophone() |
||||
|
let currentInput = getCurrentInputDevice() |
||||
|
|
||||
|
os_log("蓝牙音频设备状态: %{public}@", log: logger, type: .info, hasBluetoothDevice ? "已连接" : "未连接") |
||||
|
os_log("蓝牙麦克风状态: %{public}@", log: logger, type: .info, hasBluetoothMic ? "已连接" : "未连接") |
||||
|
os_log("当前输入设备: %{public}@", log: logger, type: .info, currentInput) |
||||
|
|
||||
|
if hasBluetoothDevice { |
||||
|
os_log("%{public}@", log: logger, type: .debug, getCurrentAudioRoute()) |
||||
|
} |
||||
|
} catch { |
||||
|
os_log("设置音频会话失败:%{public}@", log: logger, type: .error, error.localizedDescription) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func appWillEnterForeground() { |
||||
|
if let intent = currentIntent { |
||||
|
begin(intent) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func appDidEnterBackground() { |
||||
|
do { |
||||
|
try audioSession.setActive(false, options: .notifyOthersOnDeactivation) |
||||
|
os_log("音频会话已释放(应用进入后台)", log: logger, type: .debug) |
||||
|
} catch { |
||||
|
os_log("释放音频会话失败:%{public}@", log: logger, type: .error, error.localizedDescription) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 获取当前音频路由信息 |
||||
|
public func getCurrentAudioRoute() -> String { |
||||
|
let currentRoute = audioSession.currentRoute |
||||
|
var routeInfo = "当前音频路由:\n" |
||||
|
|
||||
|
// 输出设备 |
||||
|
routeInfo += "输出设备:\n" |
||||
|
for output in currentRoute.outputs { |
||||
|
routeInfo += " - \(output.portName) (\(output.portType.rawValue))\n" |
||||
|
} |
||||
|
|
||||
|
// 输入设备 |
||||
|
routeInfo += "输入设备:\n" |
||||
|
for input in currentRoute.inputs { |
||||
|
routeInfo += " - \(input.portName) (\(input.portType.rawValue))\n" |
||||
|
} |
||||
|
|
||||
|
return routeInfo |
||||
|
} |
||||
|
|
||||
|
/// 检查是否有蓝牙音频设备连接 |
||||
|
public func hasBluetoothAudioDevice() -> Bool { |
||||
|
let currentRoute = audioSession.currentRoute |
||||
|
|
||||
|
// 检查输出设备 |
||||
|
for output in currentRoute.outputs { |
||||
|
if output.portType == .bluetoothA2DP || |
||||
|
output.portType == .bluetoothHFP || |
||||
|
output.portType == .bluetoothLE { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 检查输入设备(这是关键!) |
||||
|
for input in currentRoute.inputs { |
||||
|
if input.portType == .bluetoothHFP { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
/// 检查是否有蓝牙麦克风作为输入设备 |
||||
|
public func hasBluetoothMicrophone() -> Bool { |
||||
|
let currentRoute = audioSession.currentRoute |
||||
|
|
||||
|
for input in currentRoute.inputs { |
||||
|
if input.portType == .bluetoothHFP { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
/// 获取当前输入设备信息 |
||||
|
public func getCurrentInputDevice() -> String { |
||||
|
let currentRoute = audioSession.currentRoute |
||||
|
|
||||
|
if let input = currentRoute.inputs.first { |
||||
|
return "\(input.portName) (\(input.portType.rawValue))" |
||||
|
} |
||||
|
|
||||
|
return "无输入设备" |
||||
|
} |
||||
|
|
||||
|
private func setupNotifications() { |
||||
|
NotificationCenter.default.addObserver( |
||||
|
self, |
||||
|
selector: #selector(handleAudioSessionInterruption), |
||||
|
name: AVAudioSession.interruptionNotification, |
||||
|
object: nil |
||||
|
) |
||||
|
|
||||
|
NotificationCenter.default.addObserver( |
||||
|
self, |
||||
|
selector: #selector(handleAudioRouteChange), |
||||
|
name: AVAudioSession.routeChangeNotification, |
||||
|
object: nil |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/// 处理音频中断 |
||||
|
@objc private func handleAudioSessionInterruption(notification: Notification) { |
||||
|
guard let userInfo = notification.userInfo, |
||||
|
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, |
||||
|
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
switch type { |
||||
|
case .began: |
||||
|
// 中断开始 |
||||
|
isInterrupted = true |
||||
|
os_log("音频会话被中断", log: logger, type: .debug) |
||||
|
|
||||
|
case .ended: |
||||
|
// 中断结束 |
||||
|
isInterrupted = false |
||||
|
|
||||
|
// 检查是否应该恢复 |
||||
|
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt { |
||||
|
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue) |
||||
|
if options.contains(.shouldResume), let intent = currentIntent { |
||||
|
// 恢复之前的音频意图 |
||||
|
begin(intent) |
||||
|
os_log("音频会话中断结束,已恢复", log: logger, type: .debug) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@unknown default: |
||||
|
os_log("未知的音频中断类型: %{public}@", log: logger, type: .error, "\(type)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 处理音频路由变化 |
||||
|
@objc private func handleAudioRouteChange(notification: Notification) { |
||||
|
guard let userInfo = notification.userInfo, |
||||
|
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt, |
||||
|
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
switch reason { |
||||
|
case .newDeviceAvailable: |
||||
|
os_log("新音频设备已连接", log: logger, type: .info) |
||||
|
os_log("%{public}@", log: logger, type: .debug, getCurrentAudioRoute()) |
||||
|
|
||||
|
// 重新应用当前意图以确保正确路由 |
||||
|
if let intent = currentIntent { |
||||
|
begin(intent) |
||||
|
} |
||||
|
|
||||
|
case .oldDeviceUnavailable: |
||||
|
os_log("音频设备已断开", log: logger, type: .info) |
||||
|
os_log("%{public}@", log: logger, type: .debug, getCurrentAudioRoute()) |
||||
|
|
||||
|
// 重新应用当前意图以确保正确路由 |
||||
|
if let intent = currentIntent { |
||||
|
begin(intent) |
||||
|
} |
||||
|
|
||||
|
case .categoryChange: |
||||
|
os_log("音频类别已变化", log: logger, type: .info) |
||||
|
|
||||
|
default: |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
deinit { |
||||
|
NotificationCenter.default.removeObserver(self) |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,128 +0,0 @@ |
|||||
import Foundation |
|
||||
import SpeexKit |
|
||||
import os.log |
|
||||
|
|
||||
/// 基于杰理SpeexKit的Opus音频处理器 |
|
||||
@available(iOS 13.0, *) |
|
||||
class OpusAudioProcessor: NSObject { |
|
||||
|
|
||||
// MARK: - 回调代理协议 |
|
||||
protocol AudioDataCallback: AnyObject { |
|
||||
/// 接收到解码后的PCM音频数据 |
|
||||
func onAudioDataReceived(data: Data) |
|
||||
} |
|
||||
|
|
||||
// MARK: - 属性 |
|
||||
private var isDecodingActive = false |
|
||||
private weak var callback: AudioDataCallback? |
|
||||
private let logger = OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "OpusAudioProcessor") |
|
||||
|
|
||||
// MARK: - 初始化 |
|
||||
override init() { |
|
||||
super.init() |
|
||||
|
|
||||
// 注册通知 - 这是SDK设计要求的 |
|
||||
NotificationCenter.default.addObserver( |
|
||||
self, |
|
||||
selector: #selector(onOpusDecodedData(_:)), |
|
||||
name: NSNotification.Name(rawValue: kOPUS_DECODE_DATA), |
|
||||
object: nil |
|
||||
) |
|
||||
|
|
||||
// 启用日志 |
|
||||
OpusUnit.opusIsLog(true) |
|
||||
} |
|
||||
|
|
||||
deinit { |
|
||||
stopDecoding() |
|
||||
NotificationCenter.default.removeObserver(self) |
|
||||
} |
|
||||
|
|
||||
// MARK: - 通知回调 |
|
||||
@objc private func onOpusDecodedData(_ notification: Notification) { |
|
||||
guard let pcmData = notification.object as? Data else { |
|
||||
os_log("解码回调收到无效数据", log: logger, type: .error) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 传递给回调 |
|
||||
DispatchQueue.main.async { [weak self] in |
|
||||
self?.callback?.onAudioDataReceived(data: pcmData) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// MARK: - 公共接口 |
|
||||
|
|
||||
/// 设置回调 |
|
||||
func setCallback(_ callback: AudioDataCallback?) { |
|
||||
self.callback = callback |
|
||||
} |
|
||||
|
|
||||
/// 开始Opus解码 |
|
||||
func startDecoding(sampleRate: Int32 = 16000, channels: Int32 = 1, frameSize: Int32 = 640) -> Bool { |
|
||||
// 如果已经在解码,直接返回成功 |
|
||||
if isDecodingActive { |
|
||||
os_log("Opus解码器已经在运行", log: logger, type: .debug) |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
// 设置音频参数 |
|
||||
OpusUnit.opusSetSampleRate(Int32(sampleRate), kbps: 16000, channels: Int32(channels)) |
|
||||
|
|
||||
// 必须在后台线程启动解码器(SDK要求) |
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in |
|
||||
guard let self = self else { return } |
|
||||
|
|
||||
// 启动解码器 |
|
||||
let result = OpusUnit.opusDecoderRun() |
|
||||
if result == 0 { |
|
||||
os_log("Opus解码器启动成功: 采样率=%d Hz", log: self.logger, type: .info, sampleRate) |
|
||||
self.isDecodingActive = true |
|
||||
} else { |
|
||||
os_log("Opus解码器启动失败: 错误=%d", log: self.logger, type: .error, result) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
/// 停止Opus解码 |
|
||||
func stopDecoding() -> Bool { |
|
||||
if !isDecodingActive { |
|
||||
return false |
|
||||
} |
|
||||
|
|
||||
// 必须在后台线程停止解码器 |
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in |
|
||||
guard let self = self else { return } |
|
||||
|
|
||||
let result = OpusUnit.opusDecoderStop() |
|
||||
os_log("Opus解码器停止: %d", log: self.logger, type: .info, result) |
|
||||
self.isDecodingActive = false |
|
||||
} |
|
||||
|
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
/// 处理Opus编码数据 |
|
||||
func processAudioData(_ data: Data) { |
|
||||
if !isDecodingActive { |
|
||||
os_log("Opus解码器未启动", log: logger, type: .error) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 将Opus数据送入解码器 |
|
||||
OpusUnit.opusWrite(data) |
|
||||
// 解码结果将通过通知回调给onOpusDecodedData方法 |
|
||||
} |
|
||||
|
|
||||
/// 判断解码器是否运行中 |
|
||||
func isDecoding() -> Bool { |
|
||||
return isDecodingActive |
|
||||
} |
|
||||
|
|
||||
/// 清理资源 |
|
||||
func cleanup() { |
|
||||
stopDecoding() |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,151 @@ |
|||||
|
import Foundation |
||||
|
import AVFoundation |
||||
|
import YbridOpus |
||||
|
|
||||
|
@available(iOS 13.0, *) |
||||
|
class SwiftOpusAudioProcessor: NSObject { |
||||
|
|
||||
|
protocol AudioDataCallback: AnyObject { |
||||
|
func onAudioDataReceived(data: Data) |
||||
|
} |
||||
|
|
||||
|
private var isDecodingActive = false |
||||
|
private weak var callback: AudioDataCallback? |
||||
|
|
||||
|
// Opus 解码器相关 - 使用 YbridOpus C API |
||||
|
private var decoder: OpaquePointer? |
||||
|
private var sampleRate: Int32 = 16000 |
||||
|
private var channels: Int32 = 1 |
||||
|
private let maxFrameSize: Int32 = 5760 // 120 ms @48 kHz 的最大值 |
||||
|
|
||||
|
override init() { |
||||
|
super.init() |
||||
|
} |
||||
|
|
||||
|
deinit { |
||||
|
cleanup() |
||||
|
} |
||||
|
|
||||
|
private func initializeOpusDecoder() -> Bool { |
||||
|
if decoder != nil { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
var error: Int32 = 0 |
||||
|
decoder = opus_decoder_create(sampleRate, channels, &error) |
||||
|
|
||||
|
guard error == OPUS_OK, decoder != nil else { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
private func destroyOpusDecoder() { |
||||
|
if let decoder = decoder { |
||||
|
opus_decoder_destroy(decoder) |
||||
|
self.decoder = nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func setCallback(_ callback: AudioDataCallback?) { |
||||
|
self.callback = callback |
||||
|
} |
||||
|
|
||||
|
func startDecoding(sampleRate: Int32 = 16000, channels: Int32 = 1, frameSize: Int32 = 640) -> Bool { |
||||
|
if isDecodingActive { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
self.sampleRate = sampleRate |
||||
|
self.channels = channels |
||||
|
|
||||
|
guard initializeOpusDecoder() else { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
isDecodingActive = true |
||||
|
|
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
func stopDecoding() -> Bool { |
||||
|
if !isDecodingActive { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
isDecodingActive = false |
||||
|
destroyOpusDecoder() |
||||
|
|
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
func processAudioData(_ data: Data) { |
||||
|
guard isDecodingActive, let decoder = decoder, data.count > 0 else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in |
||||
|
self?.decodeOpusPacket(data) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 使用简单稳定的 Opus 解码方法 |
||||
|
private func decodeOpusPacket(_ packet: Data) { |
||||
|
guard let decoder = decoder else { return } |
||||
|
|
||||
|
// 使用简单的直接解码方式 |
||||
|
let maxSamples = Int(maxFrameSize) |
||||
|
var pcmBuffer = [opus_int16](repeating: 0, count: maxSamples * Int(channels)) |
||||
|
|
||||
|
let decodedSamples = packet.withUnsafeBytes { packetPtr in |
||||
|
let uint8Ptr = packetPtr.bindMemory(to: UInt8.self).baseAddress |
||||
|
return opus_decode( |
||||
|
decoder, |
||||
|
uint8Ptr, |
||||
|
opus_int32(packet.count), |
||||
|
&pcmBuffer, |
||||
|
Int32(maxSamples), |
||||
|
0 |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
guard decodedSamples > 0 else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 提取解码的音频数据 |
||||
|
let samplesPerChannel = Int(decodedSamples) |
||||
|
let totalSamples = samplesPerChannel * Int(channels) |
||||
|
let outputPCM = Array(pcmBuffer[0..<totalSamples]) |
||||
|
|
||||
|
if !outputPCM.isEmpty { |
||||
|
processPCMData(outputPCM) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func processPCMData(_ pcmData: [opus_int16]) { |
||||
|
// 转换为 Data |
||||
|
let data = Data(bytes: pcmData, count: pcmData.count * MemoryLayout<opus_int16>.size) |
||||
|
|
||||
|
// 回调通知 |
||||
|
DispatchQueue.main.async { [weak self] in |
||||
|
self?.callback?.onAudioDataReceived(data: data) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func isDecoding() -> Bool { |
||||
|
return isDecodingActive |
||||
|
} |
||||
|
|
||||
|
func cleanup() { |
||||
|
stopDecoding() |
||||
|
} |
||||
|
|
||||
|
// MARK: - 公共接口 |
||||
|
|
||||
|
/// 获取当前配置信息 |
||||
|
func getCurrentConfig() -> (sampleRate: Int32, channels: Int32) { |
||||
|
return (sampleRate, channels) |
||||
|
} |
||||
|
} |
||||
@ -1,92 +0,0 @@ |
|||||
//
|
|
||||
// OpusUnit.h
|
|
||||
// QCY_Demo
|
|
||||
//
|
|
||||
// Created by 杰理科技 on 2021/7/5.
|
|
||||
// Copyright © 2021 杰理科技. All rights reserved.
|
|
||||
//
|
|
||||
|
|
||||
#import <Foundation/Foundation.h> |
|
||||
|
|
||||
NS_ASSUME_NONNULL_BEGIN |
|
||||
|
|
||||
/**
|
|
||||
* 通知:流式编码回调Opus数据 |
|
||||
* 通知数据类型:NSData(Opus) |
|
||||
*/ |
|
||||
extern NSString *kOPUS_ENCODE_DATA; |
|
||||
/**
|
|
||||
* 通知:流式解码回调PCM数据 |
|
||||
* 通知数据类型:NSData(PCM) |
|
||||
*/ |
|
||||
extern NSString *kOPUS_DECODE_DATA; |
|
||||
|
|
||||
|
|
||||
@interface OpusUnit : NSObject |
|
||||
|
|
||||
+ (void)opusIsLog:(BOOL)log; |
|
||||
|
|
||||
#pragma mark - Opus参数 |
|
||||
/**
|
|
||||
* 设置参数 |
|
||||
* @param rate 采样率 例如:48000、24000、16000、12000、8000(默认 16000) |
|
||||
* @param kbps 比特率 例如:16000、32000、64000(默认 16000) |
|
||||
* @param channel 声道 例如:1、2 (默认 1) |
|
||||
*/ |
|
||||
+(void)opusSetSampleRate:(int)rate Kbps:(int)kbps Channels:(int)channel; |
|
||||
|
|
||||
#pragma mark - Opus解码 |
|
||||
|
|
||||
/**
|
|
||||
* 直接【opus文件】转换成【pcm文件】 |
|
||||
* @param path_opus opus文件路径 |
|
||||
* @param path_pcm pcm文件路径 |
|
||||
*/ |
|
||||
+ (int)opusDecodeOPUS:(NSString *)path_opus PCM:(NSString *)path_pcm; |
|
||||
|
|
||||
/**
|
|
||||
* 流式解码【开启】 |
|
||||
*/ |
|
||||
+ (int)opusDecoderRun; |
|
||||
|
|
||||
/**
|
|
||||
* 输入Opus的数据 |
|
||||
* 通知监听“kOPUS_DECODE_DATA”获得解码后数据 |
|
||||
* 通知数据类型:NSNotification.object => NSData(PCM格式) |
|
||||
* @param data opus数据流(长度:1024) |
|
||||
*/ |
|
||||
+ (void)opusWriteData:(NSData*)data; |
|
||||
|
|
||||
/**
|
|
||||
* 流式解码【关闭】 |
|
||||
*/ |
|
||||
+ (int)opusDecoderStop; |
|
||||
|
|
||||
#pragma mark - Opus编码 |
|
||||
|
|
||||
/**
|
|
||||
* 【pcm文件】转换成【opus文件】 |
|
||||
*/ |
|
||||
+ (int)opusEncodePCM:(NSString *)path_pcm OPUS:(NSString *)path_opus; |
|
||||
|
|
||||
/**
|
|
||||
* 流式编码【开启】 |
|
||||
*/ |
|
||||
+ (int)opusEecoderRun; |
|
||||
|
|
||||
/**
|
|
||||
* 输入pcm的数据 |
|
||||
* 通知监听“kOPUS_DECODE_DATA”获得解码后数据 |
|
||||
* 通知数据类型:NSNotification.object => NSData(opus格式) |
|
||||
* @param data opus数据流(长度:1024) |
|
||||
*/ |
|
||||
+ (void)pcmWriteData:(NSData*)data; |
|
||||
|
|
||||
/**
|
|
||||
* 流式编码【关闭】 |
|
||||
*/ |
|
||||
+ (int)opusEncoderStop; |
|
||||
|
|
||||
@end |
|
||||
|
|
||||
NS_ASSUME_NONNULL_END |
|
||||
@ -1,20 +0,0 @@ |
|||||
//
|
|
||||
// SpeexKit.h
|
|
||||
// SpeexKit
|
|
||||
//
|
|
||||
// Created by zhihui liang on 2018/8/20.
|
|
||||
// Copyright © 2018年 www.zh-jieli.com. All rights reserved.
|
|
||||
//
|
|
||||
|
|
||||
#import <UIKit/UIKit.h> |
|
||||
|
|
||||
//! Project version number for SpeexKit.
|
|
||||
FOUNDATION_EXPORT double SpeexKitVersionNumber; |
|
||||
|
|
||||
//! Project version string for SpeexKit.
|
|
||||
FOUNDATION_EXPORT const unsigned char SpeexKitVersionString[]; |
|
||||
|
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <SpeexKit/PublicHeader.h>
|
|
||||
|
|
||||
#import <SpeexKit/SpeexUnit.h> |
|
||||
#import <SpeexKit/OpusUnit.h> |
|
||||
@ -1,40 +0,0 @@ |
|||||
//
|
|
||||
// SpeexUnit.h
|
|
||||
// SpeexKit
|
|
||||
//
|
|
||||
// Created by zhihui liang on 2018/8/20.
|
|
||||
// Copyright © 2018年 www.zh-jieli.com. All rights reserved.
|
|
||||
//
|
|
||||
|
|
||||
#import <Foundation/Foundation.h> |
|
||||
|
|
||||
/**
|
|
||||
* 流式解码回调PCM数据 |
|
||||
*/ |
|
||||
extern NSString *kSPEEX_DECODE_DATA; //speex decode
|
|
||||
|
|
||||
@interface SpeexUnit : NSObject |
|
||||
|
|
||||
+(void)speexIsLog:(BOOL)log; |
|
||||
|
|
||||
/**
|
|
||||
* 直接【speex文件】转换成【pcm文件】 |
|
||||
*/ |
|
||||
+(int)speexDecodeSPX:(NSString *)path_spx PCM:(NSString *)path_pcm; |
|
||||
|
|
||||
/**
|
|
||||
* 流式解码【开启】 |
|
||||
*/ |
|
||||
+(int)speexDecoderRun; |
|
||||
|
|
||||
/**
|
|
||||
* 输入Speex的数据 |
|
||||
*/ |
|
||||
+(void)speexWriteData:(NSData*)data; |
|
||||
|
|
||||
/**
|
|
||||
* 流式解码【关闭】 |
|
||||
*/ |
|
||||
+(int)speexDecoderStop; |
|
||||
|
|
||||
@end |
|
||||
Binary file not shown.
@ -1,6 +0,0 @@ |
|||||
framework module SpeexKit { |
|
||||
umbrella header "SpeexKit.h" |
|
||||
|
|
||||
export * |
|
||||
module * { export * } |
|
||||
} |
|
||||
Binary file not shown.
@ -1,126 +0,0 @@ |
|||||
# JL OPUS Plugin |
|
||||
|
|
||||
基于杰理音频编解码库的Flutter插件,提供OPUS音频格式的解码功能。 |
|
||||
|
|
||||
## 功能 |
|
||||
|
|
||||
- 支持OPUS文件解码为PCM格式 |
|
||||
- 支持OPUS数据流解码 |
|
||||
- 支持多种采样率和通道配置 |
|
||||
- 支持带协议头/不带协议头的OPUS数据 |
|
||||
|
|
||||
## 引入方式 |
|
||||
|
|
||||
在pubspec.yaml中添加依赖: |
|
||||
|
|
||||
```yaml |
|
||||
dependencies: |
|
||||
jl_opus: |
|
||||
path: ./local_plugins/jl_opus |
|
||||
``` |
|
||||
|
|
||||
## 使用方法 |
|
||||
|
|
||||
### 初始化插件 |
|
||||
|
|
||||
```dart |
|
||||
import 'package:jl_opus/jl_opus.dart'; |
|
||||
|
|
||||
final jlOpus = JlOpus(); |
|
||||
``` |
|
||||
|
|
||||
### 解码OPUS文件 |
|
||||
|
|
||||
```dart |
|
||||
Future<void> decodeOpusFile() async { |
|
||||
final inPath = '/path/to/input.opus'; |
|
||||
final outPath = '/path/to/output.pcm'; |
|
||||
|
|
||||
// 创建解码选项 |
|
||||
final option = OpusOption( |
|
||||
hasHeader: false, // 是否有协议头 |
|
||||
channel: 1, // 通道数:1=单声道,2=立体声 |
|
||||
sampleRate: 16000, // 采样率 |
|
||||
packetSize: 40, // 数据包大小(仅当hasHeader=false时生效) |
|
||||
); |
|
||||
|
|
||||
try { |
|
||||
// 开始解码文件 |
|
||||
final outFilePath = await jlOpus.decodeOpusFile(inPath, outPath, option); |
|
||||
print('解码成功: $outFilePath'); |
|
||||
} catch (e) { |
|
||||
print('解码失败: $e'); |
|
||||
} |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
### 解码OPUS数据流 |
|
||||
|
|
||||
```dart |
|
||||
Future<void> decodeOpusStream() async { |
|
||||
// 监听解码后的PCM数据 |
|
||||
jlOpus.decodedDataStream.listen((pcmData) { |
|
||||
print('收到解码后的PCM数据: ${pcmData.length} 字节'); |
|
||||
// 处理解码后的PCM数据 |
|
||||
}); |
|
||||
|
|
||||
// 创建解码选项 |
|
||||
final option = OpusOption( |
|
||||
hasHeader: false, |
|
||||
channel: 1, |
|
||||
sampleRate: 16000, |
|
||||
packetSize: 40, |
|
||||
); |
|
||||
|
|
||||
// 开始解码数据流 |
|
||||
final success = await jlOpus.startDecodeStream(option); |
|
||||
if (success) { |
|
||||
print('开始解码数据流'); |
|
||||
|
|
||||
// 写入OPUS数据进行解码 |
|
||||
final opusData = Uint8List.fromList([/* OPUS数据 */]); |
|
||||
await jlOpus.writeAudioStream(opusData); |
|
||||
|
|
||||
// 数据处理完成后停止解码 |
|
||||
await jlOpus.stopDecodeStream(); |
|
||||
} |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
### 监听事件 |
|
||||
|
|
||||
```dart |
|
||||
void listenToEvents() { |
|
||||
jlOpus.eventStream.listen((event) { |
|
||||
switch (event.event) { |
|
||||
case 'onStart': |
|
||||
print('开始${event.type == "file" ? "文件" : "流"}解码'); |
|
||||
break; |
|
||||
case 'onComplete': |
|
||||
print('完成${event.type == "file" ? "文件" : "流"}解码'); |
|
||||
if (event.filePath != null) { |
|
||||
print('输出文件: ${event.filePath}'); |
|
||||
} |
|
||||
break; |
|
||||
case 'onError': |
|
||||
print('解码错误: [${event.errorCode}] ${event.errorMessage}'); |
|
||||
break; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
### 资源释放 |
|
||||
|
|
||||
```dart |
|
||||
Future<void> disposePlugin() async { |
|
||||
await jlOpus.dispose(); |
|
||||
print('插件资源已释放'); |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
## 注意事项 |
|
||||
|
|
||||
1. OPUS解码需要正确设置参数,特别是采样率和通道数必须与原始音频匹配 |
|
||||
2. 使用前请确保jl_audio_decode_V2.0.0_20005.aar包已添加到libs目录 |
|
||||
3. 本插件仅支持Android平台 |
|
||||
@ -1,62 +0,0 @@ |
|||||
plugins { |
|
||||
// Android Library 插件 |
|
||||
id("com.android.library") |
|
||||
// Kotlin Android 插件 |
|
||||
id("org.jetbrains.kotlin.android") |
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1" |
|
||||
} |
|
||||
|
|
||||
// 添加mavenLocal()仓库 |
|
||||
repositories { |
|
||||
mavenLocal() |
|
||||
google() |
|
||||
mavenCentral() |
|
||||
} |
|
||||
|
|
||||
android { |
|
||||
// 命名空间,对应你插件的包名(需与代码内包名保持一致) |
|
||||
namespace = "com.yunqiinnovation.jl_opus" |
|
||||
|
|
||||
// 目标 SDK 版本 |
|
||||
compileSdk = 35 |
|
||||
|
|
||||
defaultConfig { |
|
||||
// 最低 SDK 版本 |
|
||||
minSdk = 21 |
|
||||
targetSdk = 33 |
|
||||
} |
|
||||
|
|
||||
// Java 语言级别兼容配置 |
|
||||
compileOptions { |
|
||||
sourceCompatibility = JavaVersion.VERSION_11 |
|
||||
targetCompatibility = JavaVersion.VERSION_11 |
|
||||
} |
|
||||
|
|
||||
// Kotlin 语言级别 |
|
||||
kotlinOptions { |
|
||||
jvmTarget = "11" |
|
||||
} |
|
||||
|
|
||||
// 添加packaging配置,排除冲突文件 |
|
||||
packaging { |
|
||||
resources { |
|
||||
excludes.add("META-INF/DEPENDENCIES") |
|
||||
excludes.add("META-INF/LICENSE") |
|
||||
excludes.add("META-INF/LICENSE.txt") |
|
||||
excludes.add("META-INF/license.txt") |
|
||||
excludes.add("META-INF/NOTICE") |
|
||||
excludes.add("META-INF/NOTICE.txt") |
|
||||
excludes.add("META-INF/notice.txt") |
|
||||
excludes.add("META-INF/*.kotlin_module") |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
dependencies { |
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10") |
|
||||
implementation("androidx.annotation:annotation:1.5.0") |
|
||||
implementation("androidx.core:core-ktx:1.9.0") |
|
||||
// 使用从本地Maven仓库引用的AAR依赖 |
|
||||
implementation("com.jieli.audio:jl_audio_decode:2.0.0") |
|
||||
} |
|
||||
|
|
||||
@ -1 +0,0 @@ |
|||||
rootProject.name = "jl_opus" |
|
||||
@ -1,9 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||
package="com.yunqiinnovation.jl_opus"> |
|
||||
|
|
||||
<!-- 外部存储权限 --> |
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> |
|
||||
</manifest> |
|
||||
@ -1,426 +0,0 @@ |
|||||
package com.yunqiinnovation.jl_opus |
|
||||
|
|
||||
import android.content.Context |
|
||||
import android.os.Handler |
|
||||
import android.os.Looper |
|
||||
import android.util.Log |
|
||||
import androidx.annotation.NonNull |
|
||||
import com.jieli.jl_audio_decode.opus.model.OpusOption |
|
||||
import com.jieli.jl_audio_decode.callback.OnDecodeStreamCallback |
|
||||
import com.jieli.jl_audio_decode.callback.OnStateCallback |
|
||||
import com.jieli.jl_audio_decode.opus.OpusManager |
|
||||
import com.jieli.jl_audio_decode.exceptions.OpusException |
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin |
|
||||
import io.flutter.plugin.common.EventChannel |
|
||||
import io.flutter.plugin.common.MethodCall |
|
||||
import io.flutter.plugin.common.MethodChannel |
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler |
|
||||
import io.flutter.plugin.common.MethodChannel.Result |
|
||||
import java.io.File |
|
||||
|
|
||||
/** JlOpusPlugin */ |
|
||||
class JlOpusPlugin : FlutterPlugin, MethodCallHandler { |
|
||||
private lateinit var channel: MethodChannel |
|
||||
private lateinit var eventChannel: EventChannel |
|
||||
private lateinit var context: Context |
|
||||
private var opusManager: OpusManager? = null |
|
||||
private var streamSink: EventChannel.EventSink? = null |
|
||||
|
|
||||
// 修正1:正确的主构造函数写法 |
|
||||
constructor() { |
|
||||
opusManager = OpusManager() |
|
||||
Log.d(TAG, "OpusManager初始化成功") |
|
||||
} |
|
||||
|
|
||||
// 添加主线程Handler用于确保在主线程回调 |
|
||||
private val mainHandler = Handler(Looper.getMainLooper()) |
|
||||
|
|
||||
interface CallbackOpus { |
|
||||
|
|
||||
fun onAudioDataReceivedOpus(data: ByteArray) |
|
||||
|
|
||||
} |
|
||||
|
|
||||
companion object { |
|
||||
private const val TAG = "JlOpusPlugin" |
|
||||
private const val CHANNEL_NAME = "com.yunqiinnovation.jl_opus" |
|
||||
private const val EVENT_CHANNEL_NAME = "com.yunqiinnovation.jl_opus/events" |
|
||||
|
|
||||
} |
|
||||
|
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { |
|
||||
|
|
||||
context = flutterPluginBinding.applicationContext |
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) |
|
||||
channel.setMethodCallHandler(this) |
|
||||
|
|
||||
eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, EVENT_CHANNEL_NAME) |
|
||||
|
|
||||
eventChannel.setStreamHandler(object : EventChannel.StreamHandler { |
|
||||
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { |
|
||||
streamSink = events |
|
||||
} |
|
||||
|
|
||||
override fun onCancel(arguments: Any?) { |
|
||||
streamSink = null |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// 初始化OpusManager |
|
||||
try { |
|
||||
opusManager = OpusManager() |
|
||||
Log.d(TAG, "OpusManager初始化成功") |
|
||||
} catch (e: OpusException) { |
|
||||
Log.e(TAG, "OpusManager初始化失败: ${e.message}", e) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 在主线程上安全发送事件 |
|
||||
private fun sendEventOnMainThread(event: Map<String, Any?>) { |
|
||||
if (streamSink == null) return |
|
||||
|
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) { |
|
||||
// 如果已经在主线程,直接发送 |
|
||||
streamSink?.success(event) |
|
||||
} else { |
|
||||
// 否则切换到主线程 |
|
||||
mainHandler.post { |
|
||||
streamSink?.success(event) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 在主线程上安全返回结果 |
|
||||
private fun sendResultOnMainThread(result: Result, value: Any?) { |
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) { |
|
||||
// 如果已经在主线程,直接返回 |
|
||||
result.success(value) |
|
||||
} else { |
|
||||
// 否则切换到主线程 |
|
||||
mainHandler.post { |
|
||||
result.success(value) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 在主线程上安全返回错误 |
|
||||
private fun sendErrorOnMainThread( |
|
||||
result: Result, |
|
||||
errorCode: String, |
|
||||
errorMessage: String, |
|
||||
errorDetails: Any? |
|
||||
) { |
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) { |
|
||||
// 如果已经在主线程,直接返回 |
|
||||
result.error(errorCode, errorMessage, errorDetails) |
|
||||
} else { |
|
||||
// 否则切换到主线程 |
|
||||
mainHandler.post { |
|
||||
result.error(errorCode, errorMessage, errorDetails) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { |
|
||||
if (opusManager == null) { |
|
||||
result.error("OPUS_MANAGER_NULL", "OpusManager未初始化", null) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
when (call.method) { |
|
||||
"initOpusDecoder" -> { |
|
||||
result.success(opusManager != null) |
|
||||
} |
|
||||
|
|
||||
"decodeOpusFile" -> { |
|
||||
val inPath = call.argument<String>("inPath") |
|
||||
val outPath = call.argument<String>("outPath") |
|
||||
val hasHeader = call.argument<Boolean>("hasHeader") ?: false |
|
||||
val channel = call.argument<Int>("channel") ?: 1 |
|
||||
val sampleRate = call.argument<Int>("sampleRate") ?: 16000 |
|
||||
val packetSize = call.argument<Int>("packetSize") ?: 40 |
|
||||
|
|
||||
if (inPath == null || outPath == null) { |
|
||||
result.error("INVALID_ARGS", "输入或输出路径不能为空", null) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 确保输入文件存在 |
|
||||
val inFile = File(inPath) |
|
||||
if (!inFile.exists()) { |
|
||||
result.error("FILE_NOT_FOUND", "输入文件不存在: $inPath", null) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 创建解码参数 |
|
||||
val option = OpusOption() |
|
||||
.setHasHead(hasHeader) |
|
||||
.setChannel(channel) |
|
||||
.setSampleRate(sampleRate) |
|
||||
.setPacketSize(packetSize) |
|
||||
|
|
||||
Log.d(TAG, "开始解码文件, 参数: $option, 输入: $inPath, 输出: $outPath") |
|
||||
|
|
||||
opusManager?.decodeFile(inPath, outPath, option, object : OnStateCallback { |
|
||||
override fun onStart() { |
|
||||
Log.d(TAG, "文件解码开始") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onStart", |
|
||||
"type" to "file" |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
override fun onComplete(outFilePath: String?) { |
|
||||
Log.d(TAG, "文件解码完成: $outFilePath") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onComplete", |
|
||||
"type" to "file", |
|
||||
"filePath" to outFilePath |
|
||||
) |
|
||||
) |
|
||||
sendResultOnMainThread(result, outFilePath) |
|
||||
} |
|
||||
|
|
||||
override fun onError(code: Int, message: String?) { |
|
||||
Log.e(TAG, "文件解码错误: [$code] $message") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onError", |
|
||||
"type" to "file", |
|
||||
"code" to code, |
|
||||
"message" to message |
|
||||
) |
|
||||
) |
|
||||
sendErrorOnMainThread( |
|
||||
result, |
|
||||
"DECODE_ERROR", |
|
||||
"解码出错: [$code] $message", |
|
||||
null |
|
||||
) |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
"startDecodeStream" -> { |
|
||||
val hasHeader = call.argument<Boolean>("hasHeader") ?: false |
|
||||
val channel = call.argument<Int>("channel") ?: 1 |
|
||||
val sampleRate = call.argument<Int>("sampleRate") ?: 16000 |
|
||||
val packetSize = call.argument<Int>("packetSize") ?: 40 |
|
||||
|
|
||||
// 如果已经在解码流,先停止 |
|
||||
if (opusManager?.isDecodeStream == true) { |
|
||||
opusManager?.stopDecodeStream() |
|
||||
} |
|
||||
|
|
||||
// 创建解码参数 |
|
||||
val option = OpusOption() |
|
||||
.setHasHead(hasHeader) |
|
||||
.setChannel(channel) |
|
||||
.setSampleRate(sampleRate) |
|
||||
.setPacketSize(packetSize) |
|
||||
|
|
||||
Log.d(TAG, "开始解码数据流, 参数: $option") |
|
||||
|
|
||||
opusManager?.startDecodeStream(option, object : OnDecodeStreamCallback { |
|
||||
override fun onDecodeStream(data: ByteArray?) { |
|
||||
if (data != null) { |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onDecodeStream", |
|
||||
"data" to data |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
override fun onStart() { |
|
||||
Log.d(TAG, "数据流解码开始") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onStart", |
|
||||
"type" to "stream" |
|
||||
) |
|
||||
) |
|
||||
sendResultOnMainThread(result, true) |
|
||||
} |
|
||||
|
|
||||
override fun onComplete(outPath: String?) { |
|
||||
Log.d(TAG, "数据流解码完成: $outPath") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onComplete", |
|
||||
"type" to "stream" |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
override fun onError(code: Int, message: String?) { |
|
||||
Log.e(TAG, "数据流解码错误: [$code] $message") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onError", |
|
||||
"type" to "stream", |
|
||||
"code" to code, |
|
||||
"message" to message |
|
||||
) |
|
||||
) |
|
||||
sendErrorOnMainThread( |
|
||||
result, |
|
||||
"DECODE_STREAM_ERROR", |
|
||||
"数据流解码出错: [$code] $message", |
|
||||
null |
|
||||
) |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
"stopDecodeStream" -> { |
|
||||
if (opusManager?.isDecodeStream == true) { |
|
||||
opusManager?.stopDecodeStream() |
|
||||
result.success(true) |
|
||||
} else { |
|
||||
result.success(false) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
"writeAudioStream" -> { |
|
||||
val data = call.argument<ByteArray>("data") |
|
||||
if (data == null) { |
|
||||
result.error("INVALID_ARGS", "音频数据不能为空", null) |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
if (opusManager?.isDecodeStream == true) { |
|
||||
opusManager?.writeAudioStream(data) |
|
||||
result.success(true) |
|
||||
} else { |
|
||||
result.error("NOT_DECODING", "当前没有处于解码状态", null) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
"isDecoding" -> { |
|
||||
result.success(opusManager?.isDecodeStream == true) |
|
||||
} |
|
||||
|
|
||||
"dispose" -> { |
|
||||
opusManager?.let { |
|
||||
if (it.isDecodeStream) { |
|
||||
it.stopDecodeStream() |
|
||||
} |
|
||||
it.release() |
|
||||
} |
|
||||
opusManager = null |
|
||||
result.success(true) |
|
||||
} |
|
||||
|
|
||||
else -> { |
|
||||
result.notImplemented() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
fun handleDecodeRequest(opusdata: ByteArray) { |
|
||||
|
|
||||
// Log.d(TAG, "handleDecodeRequest${opusdata}") |
|
||||
if (opusdata == null) { |
|
||||
|
|
||||
return |
|
||||
} |
|
||||
if (opusManager?.isDecodeStream == true) { |
|
||||
opusManager?.writeAudioStream(opusdata) |
|
||||
|
|
||||
} else { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
fun startOpusDecodeStream( |
|
||||
hasHeader: Boolean, |
|
||||
channel: Int, |
|
||||
sampleRate: Int, |
|
||||
packetSize: Int, |
|
||||
callback: CallbackOpus |
|
||||
|
|
||||
) { |
|
||||
Log.d(TAG, "开始解码数据流,startOpusDecodeStream: $opusManager") |
|
||||
// 如果已经在解码流,先停止 |
|
||||
if (opusManager?.isDecodeStream == true) { |
|
||||
opusManager?.stopDecodeStream() |
|
||||
} |
|
||||
|
|
||||
// 创建解码参数 |
|
||||
val option = OpusOption() |
|
||||
.setHasHead(hasHeader) |
|
||||
.setChannel(channel) |
|
||||
.setSampleRate(sampleRate) |
|
||||
.setPacketSize(packetSize) |
|
||||
|
|
||||
Log.d(TAG, "开始解码数据流, 参数: $option") |
|
||||
|
|
||||
opusManager?.startDecodeStream(option, object : OnDecodeStreamCallback { |
|
||||
override fun onDecodeStream(data: ByteArray?) { |
|
||||
if (data != null) { |
|
||||
//Log.d(TAG, "onDecodeStream${data}") |
|
||||
callback.onAudioDataReceivedOpus(data) |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onDecodeStream", |
|
||||
"data" to data |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
override fun onStart() { |
|
||||
Log.d(TAG, "数据流解码开始") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onStart", |
|
||||
"type" to "stream" |
|
||||
) |
|
||||
) |
|
||||
|
|
||||
} |
|
||||
|
|
||||
override fun onComplete(outPath: String?) { |
|
||||
Log.d(TAG, "数据流解码完成: $outPath") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onComplete", |
|
||||
"type" to "stream" |
|
||||
) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
override fun onError(code: Int, message: String?) { |
|
||||
Log.e(TAG, "数据流解码错误: [$code] $message") |
|
||||
sendEventOnMainThread( |
|
||||
mapOf( |
|
||||
"event" to "onError", |
|
||||
"type" to "stream", |
|
||||
"code" to code, |
|
||||
"message" to message |
|
||||
) |
|
||||
) |
|
||||
|
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { |
|
||||
|
|
||||
channel.setMethodCallHandler(null) |
|
||||
eventChannel.setStreamHandler(null) |
|
||||
opusManager?.let { |
|
||||
if (it.isDecodeStream) { |
|
||||
it.stopDecodeStream() |
|
||||
} |
|
||||
it.release() |
|
||||
} |
|
||||
opusManager = null |
|
||||
} |
|
||||
} |
|
||||
Binary file not shown.
@ -1,221 +0,0 @@ |
|||||
import 'dart:async'; |
|
||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
|
|
||||
/// OPUS解码参数选项 |
|
||||
class OpusOption { |
|
||||
/// 是否具有协议头 |
|
||||
final bool hasHeader; |
|
||||
|
|
||||
/// 通道数。取值范围:[1, 2] |
|
||||
final int channel; |
|
||||
|
|
||||
/// 采样率。参考值:{8000, 16000, 24000, 32000, 44100, 48000} |
|
||||
final int sampleRate; |
|
||||
|
|
||||
/// 数据包长度。仅hasHeader = false时生效。 |
|
||||
final int packetSize; |
|
||||
|
|
||||
/// 构造函数 |
|
||||
OpusOption({ |
|
||||
this.hasHeader = false, |
|
||||
this.channel = 1, |
|
||||
this.sampleRate = 16000, |
|
||||
this.packetSize = 40, |
|
||||
}); |
|
||||
|
|
||||
/// 转换为参数Map |
|
||||
Map<String, dynamic> toMap() { |
|
||||
return { |
|
||||
'hasHeader': hasHeader, |
|
||||
'channel': channel, |
|
||||
'sampleRate': sampleRate, |
|
||||
'packetSize': packetSize, |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 杰理OPUS编解码器事件 |
|
||||
class OpusEvent { |
|
||||
/// 事件类型 |
|
||||
final String event; |
|
||||
|
|
||||
/// 数据类型 (file或stream) |
|
||||
final String? type; |
|
||||
|
|
||||
/// 解码后的数据 (仅在流解码时有效) |
|
||||
final Uint8List? data; |
|
||||
|
|
||||
/// 输出文件路径 (仅在文件解码完成时有效) |
|
||||
final String? filePath; |
|
||||
|
|
||||
/// 错误码 (仅在发生错误时有效) |
|
||||
final int? errorCode; |
|
||||
|
|
||||
/// 错误信息 (仅在发生错误时有效) |
|
||||
final String? errorMessage; |
|
||||
|
|
||||
/// 构造函数 |
|
||||
OpusEvent({ |
|
||||
required this.event, |
|
||||
this.type, |
|
||||
this.data, |
|
||||
this.filePath, |
|
||||
this.errorCode, |
|
||||
this.errorMessage, |
|
||||
}); |
|
||||
|
|
||||
/// 从Map构造对象 |
|
||||
factory OpusEvent.fromMap(Map<dynamic, dynamic> map) { |
|
||||
return OpusEvent( |
|
||||
event: map['event'], |
|
||||
type: map['type'], |
|
||||
data: map['data'], |
|
||||
filePath: map['filePath'], |
|
||||
errorCode: map['code'], |
|
||||
errorMessage: map['message'], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 杰理OPUS编解码插件 |
|
||||
class JlOpus { |
|
||||
static const MethodChannel _methodChannel = |
|
||||
MethodChannel('com.yunqiinnovation.jl_opus'); |
|
||||
static const EventChannel _eventChannel = |
|
||||
EventChannel('com.yunqiinnovation.jl_opus/events'); |
|
||||
static const EventChannel _eventChannel1 = |
|
||||
EventChannel('com.yunqiinnovation.jl_opus/events1'); |
|
||||
|
|
||||
/// 事件流 |
|
||||
Stream<OpusEvent>? _eventStream; |
|
||||
|
|
||||
/// 获取事件流 |
|
||||
Stream<OpusEvent> get eventStream { |
|
||||
_eventStream ??= _eventChannel.receiveBroadcastStream().map((event) { |
|
||||
if (event is Map) { |
|
||||
return OpusEvent.fromMap(event); |
|
||||
} |
|
||||
throw PlatformException( |
|
||||
code: 'INVALID_EVENT', |
|
||||
message: '无效的事件数据', |
|
||||
); |
|
||||
}); |
|
||||
return _eventStream!; |
|
||||
} |
|
||||
|
|
||||
/// 解码后的数据流 |
|
||||
final StreamController<Uint8List> _decodedDataController = |
|
||||
StreamController<Uint8List>.broadcast(); |
|
||||
|
|
||||
/// 获取解码数据流 |
|
||||
Stream<Uint8List> get decodedDataStream => _decodedDataController.stream; |
|
||||
|
|
||||
/// 构造函数,设置事件监听 |
|
||||
JlOpus() { |
|
||||
_setupEventListener(); |
|
||||
} |
|
||||
|
|
||||
/// 设置事件监听 |
|
||||
void _setupEventListener() { |
|
||||
eventStream.listen((event) { |
|
||||
if (event.event == 'onDecodeStream' && event.data != null) { |
|
||||
_decodedDataController.add(event.data!); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// 初始化OPUS解码器 |
|
||||
Future<bool> initOpusDecoder() async { |
|
||||
try { |
|
||||
final result = await _methodChannel.invokeMethod<bool>('initOpusDecoder'); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 解码OPUS文件 |
|
||||
/// |
|
||||
/// [inPath] 输入文件路径 |
|
||||
/// [outPath] 输出文件路径 |
|
||||
/// [option] 解码参数 |
|
||||
Future<String?> decodeOpusFile( |
|
||||
String inPath, String outPath, OpusOption option) async { |
|
||||
try { |
|
||||
final result = await _methodChannel.invokeMethod<String>( |
|
||||
'decodeOpusFile', |
|
||||
{ |
|
||||
'inPath': inPath, |
|
||||
'outPath': outPath, |
|
||||
...option.toMap(), |
|
||||
}, |
|
||||
); |
|
||||
return result; |
|
||||
} catch (e) { |
|
||||
rethrow; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 开始解码OPUS数据流 |
|
||||
/// |
|
||||
/// [option] 解码参数 |
|
||||
Future<bool> startDecodeStream(OpusOption option) async { |
|
||||
try { |
|
||||
final result = await _methodChannel.invokeMethod<bool>( |
|
||||
'startDecodeStream', |
|
||||
option.toMap(), |
|
||||
); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
rethrow; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 停止解码OPUS数据流 |
|
||||
Future<bool> stopDecodeStream() async { |
|
||||
try { |
|
||||
final result = |
|
||||
await _methodChannel.invokeMethod<bool>('stopDecodeStream'); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 写入OPUS音频数据进行解码 |
|
||||
/// |
|
||||
/// [data] OPUS音频数据 |
|
||||
Future<bool> writeAudioStream(Uint8List data) async { |
|
||||
try { |
|
||||
final result = await _methodChannel.invokeMethod<bool>( |
|
||||
'writeAudioStream', |
|
||||
{'data': data}, |
|
||||
); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
rethrow; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 判断是否正在解码 |
|
||||
Future<bool> isDecoding() async { |
|
||||
try { |
|
||||
final result = await _methodChannel.invokeMethod<bool>('isDecoding'); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 释放资源 |
|
||||
Future<bool> dispose() async { |
|
||||
try { |
|
||||
_decodedDataController.close(); |
|
||||
final result = await _methodChannel.invokeMethod<bool>('dispose'); |
|
||||
return result ?? false; |
|
||||
} catch (e) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
name: jl_opus |
|
||||
description: 杰理OPUS编解码库Flutter插件 |
|
||||
version: 0.0.1 |
|
||||
homepage: |
|
||||
|
|
||||
environment: |
|
||||
sdk: ">=2.12.0 <3.0.0" |
|
||||
flutter: ">=2.0.0" |
|
||||
|
|
||||
dependencies: |
|
||||
flutter: |
|
||||
sdk: flutter |
|
||||
path_provider: ^2.0.0 |
|
||||
permission_handler: ^11.4.0 |
|
||||
|
|
||||
dev_dependencies: |
|
||||
flutter_test: |
|
||||
sdk: flutter |
|
||||
|
|
||||
flutter: |
|
||||
plugin: |
|
||||
platforms: |
|
||||
android: |
|
||||
package: com.yunqiinnovation.jl_opus |
|
||||
pluginClass: JlOpusPlugin |
|
||||
@ -1,253 +0,0 @@ |
|||||
# OpenAI Service Plugin |
|
||||
|
|
||||
一个用于Flutter应用的OpenAI服务插件,支持Android和iOS平台。 |
|
||||
|
|
||||
## 功能 |
|
||||
|
|
||||
- 支持文本生成(completions) |
|
||||
- 支持流式输出(streaming) |
|
||||
- 支持函数调用(function calling) |
|
||||
- 支持自定义API基础URL |
|
||||
- 支持自定义模型选择 |
|
||||
|
|
||||
## 安装 |
|
||||
|
|
||||
在你的`pubspec.yaml`文件中添加以下依赖: |
|
||||
|
|
||||
```yaml |
|
||||
dependencies: |
|
||||
open_ai_service: |
|
||||
path: 本地路径/open_ai_service |
|
||||
``` |
|
||||
|
|
||||
## 使用方法 |
|
||||
|
|
||||
### 初始化服务 |
|
||||
|
|
||||
```dart |
|
||||
import 'package:open_ai_service/open_ai_service.dart'; |
|
||||
|
|
||||
final openAIService = OpenAIService(); |
|
||||
|
|
||||
// 初始化服务 |
|
||||
await openAIService.initialize( |
|
||||
apiKey: 'your_openai_api_key', |
|
||||
baseUrl: 'https://api.openai.com/v1/chat/completions', // 可选 |
|
||||
model: 'gpt-4-turbo', // 可选 |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
### 发送非流式请求 |
|
||||
|
|
||||
```dart |
|
||||
// 创建消息 |
|
||||
final userMessage = await openAIService.createUserMessage('你好,请介绍一下自己'); |
|
||||
|
|
||||
// 发送请求 |
|
||||
final response = await openAIService.sendMessage( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个有用的AI助手', |
|
||||
); |
|
||||
|
|
||||
print('AI回复: $response'); |
|
||||
``` |
|
||||
|
|
||||
### 发送流式请求(回调方式) |
|
||||
|
|
||||
```dart |
|
||||
// 创建消息 |
|
||||
final userMessage = await openAIService.createUserMessage('写一个短故事'); |
|
||||
|
|
||||
// 发送流式请求 |
|
||||
await openAIService.sendMessageStream( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个善于讲故事的AI助手', |
|
||||
); |
|
||||
|
|
||||
// 处理事件 |
|
||||
final subscription = openAIService.processEvents( |
|
||||
onToken: (token) { |
|
||||
// 处理每个返回的token |
|
||||
print(token); |
|
||||
}, |
|
||||
onComplete: () { |
|
||||
// 处理完成事件 |
|
||||
print('生成完成'); |
|
||||
}, |
|
||||
onError: (error) { |
|
||||
// 处理错误 |
|
||||
print('错误: $error'); |
|
||||
}, |
|
||||
onFunctionCall: (functionCall) { |
|
||||
// 处理函数调用 |
|
||||
print('函数调用: ${functionCall['name']}'); |
|
||||
}, |
|
||||
); |
|
||||
|
|
||||
// 在不需要时取消订阅 |
|
||||
subscription.cancel(); |
|
||||
``` |
|
||||
|
|
||||
### 发送流式请求(Stream方式) |
|
||||
|
|
||||
```dart |
|
||||
// 创建消息 |
|
||||
final userMessage = await openAIService.createUserMessage('写一个短故事'); |
|
||||
|
|
||||
// 获取字符串流 |
|
||||
final stream = openAIService.streamMessage( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个善于讲故事的AI助手', |
|
||||
); |
|
||||
|
|
||||
// 使用流 |
|
||||
final StringBuilder responseBuilder = StringBuilder(); |
|
||||
|
|
||||
stream.listen( |
|
||||
(token) { |
|
||||
// 处理每个token |
|
||||
responseBuilder.write(token); |
|
||||
print(token); // 实时输出 |
|
||||
}, |
|
||||
onDone: () { |
|
||||
// 流结束 |
|
||||
print('完整回复: ${responseBuilder.toString()}'); |
|
||||
}, |
|
||||
onError: (error) { |
|
||||
// 错误处理 |
|
||||
print('错误: $error'); |
|
||||
} |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
### 注册函数 |
|
||||
|
|
||||
```dart |
|
||||
// 注册一个函数 |
|
||||
await openAIService.registerFunction( |
|
||||
name: 'get_weather', |
|
||||
description: '获取指定城市的天气信息', |
|
||||
parameters: { |
|
||||
'type': 'object', |
|
||||
'properties': { |
|
||||
'city': { |
|
||||
'type': 'string', |
|
||||
'description': '城市名称', |
|
||||
}, |
|
||||
'date': { |
|
||||
'type': 'string', |
|
||||
'description': '日期,格式为YYYY-MM-DD', |
|
||||
}, |
|
||||
}, |
|
||||
'required': ['city'], |
|
||||
}, |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
### 处理函数调用 |
|
||||
|
|
||||
```dart |
|
||||
// 创建消息 |
|
||||
final userMessage = await openAIService.createUserMessage('明天北京的天气如何?'); |
|
||||
|
|
||||
// 发送流式请求 |
|
||||
await openAIService.sendMessageStream( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个有用的AI助手', |
|
||||
); |
|
||||
|
|
||||
// 处理事件 |
|
||||
openAIService.processEvents( |
|
||||
onToken: (token) { |
|
||||
print(token); |
|
||||
}, |
|
||||
onComplete: () { |
|
||||
print('生成完成'); |
|
||||
}, |
|
||||
onError: (error) { |
|
||||
print('错误: $error'); |
|
||||
}, |
|
||||
onFunctionCall: (functionCall) { |
|
||||
// 处理函数调用 |
|
||||
final name = functionCall['name']; |
|
||||
final arguments = functionCall['arguments']; |
|
||||
|
|
||||
print('收到函数调用: $name, 参数: $arguments'); |
|
||||
|
|
||||
// 假设处理了函数调用并获得结果 |
|
||||
final result = '{"temperature": 25, "condition": "sunny"}'; |
|
||||
|
|
||||
// 发送函数调用结果 |
|
||||
openAIService.sendFunctionCallResult( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个有用的AI助手', |
|
||||
functionCall: functionCall, |
|
||||
functionResult: result, |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
### 使用Stream API处理函数调用 |
|
||||
|
|
||||
```dart |
|
||||
// 创建消息和响应处理器 |
|
||||
final userMessage = await openAIService.createUserMessage('明天北京的天气如何?'); |
|
||||
final responseBuilder = StringBuilder(); |
|
||||
|
|
||||
// 处理事件流以捕获函数调用 |
|
||||
final subscription = openAIService.processEvents( |
|
||||
onFunctionCall: (functionCall) async { |
|
||||
// 取消当前事件监听 |
|
||||
subscription.cancel(); |
|
||||
|
|
||||
// 处理函数调用 |
|
||||
final name = functionCall['name']; |
|
||||
final arguments = functionCall['arguments']; |
|
||||
|
|
||||
print('收到函数调用: $name, 参数: $arguments'); |
|
||||
|
|
||||
// 假设处理了函数调用并获得结果 |
|
||||
final result = '{"temperature": 25, "condition": "sunny"}'; |
|
||||
|
|
||||
// 使用Stream API发送函数调用结果 |
|
||||
final resultStream = openAIService.streamFunctionResult( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个有用的AI助手', |
|
||||
functionCall: functionCall, |
|
||||
functionResult: result, |
|
||||
); |
|
||||
|
|
||||
// 处理结果流 |
|
||||
resultStream.listen( |
|
||||
(token) { |
|
||||
responseBuilder.write(token); |
|
||||
print(token); // 实时输出 |
|
||||
}, |
|
||||
onDone: () { |
|
||||
print('完整回复: ${responseBuilder.toString()}'); |
|
||||
}, |
|
||||
onError: (error) { |
|
||||
print('错误: $error'); |
|
||||
} |
|
||||
); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 启动请求 |
|
||||
await openAIService.sendMessageStream( |
|
||||
messages: [userMessage], |
|
||||
systemPrompt: '你是一个有用的AI助手', |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
## 注意事项 |
|
||||
|
|
||||
1. 确保在使用前已正确初始化服务 |
|
||||
2. 对于流式请求,确保在不需要时取消订阅 |
|
||||
3. 处理函数调用时,确保提供有效的结果格式 |
|
||||
4. 网络请求可能会失败,请确保加入适当的错误处理 |
|
||||
|
|
||||
## 许可证 |
|
||||
|
|
||||
[MIT License](LICENSE) |
|
||||
@ -1,249 +1,366 @@ |
|||||
import Foundation |
import Foundation |
||||
|
|
||||
/// 工具类型枚举 |
/// 工具类型枚举 |
||||
enum ToolType { |
public enum ToolType { |
||||
case localFunction // 本地函数 |
case localFunction // 本地函数 |
||||
case mcpTool // MCP工具 |
case mcpTool // MCP工具 |
||||
} |
} |
||||
|
|
||||
/// 函数处理器协议 |
/// 函数处理器协议 |
||||
protocol FunctionHandler { |
public protocol FunctionHandler { |
||||
/// 处理函数调用 |
/// 处理函数调用 |
||||
/// - Parameters: |
/// - Parameter arguments: 函数参数,字典格式 |
||||
/// - arguments: 函数参数,Dictionary格式 |
|
||||
/// - Returns: 函数执行结果,字符串 |
/// - Returns: 函数执行结果,字符串 |
||||
func handle(arguments: [String: Any]) async throws -> String |
func handle(arguments: [String: Any]) async throws -> String |
||||
} |
} |
||||
|
|
||||
/// MCPClient - iOS版本,负责连接到Model Context Protocol服务器和管理本地函数 |
/// 单个MCP子客户端 |
||||
public class MCPClient { |
/// 负责连接到一个具体的MCP服务器并管理其工具 |
||||
private let TAG = "MCPClient" |
public class MCPSubClient { |
||||
|
private let tag: String |
||||
|
private let serverId: String |
||||
|
private let serverUrl: String |
||||
|
|
||||
// 本地函数Map,函数名 -> 处理器 |
// 服务器提供的工具列表 |
||||
private var localFunctions = [String: FunctionHandler]() |
private var tools: [[String: Any]] = [] |
||||
|
|
||||
// 工具定义Map |
// 工具Map列表,用于OpenAI请求 |
||||
private var toolDefinitions = [[String: Any]]() |
private var toolMaps: [[String: Any]] = [] |
||||
|
|
||||
// 是否已连接 |
// 是否已连接 |
||||
private var isConnected = false |
private var isConnectedFlag = false |
||||
|
|
||||
// 系统功能处理器 |
// 工具名称到工具的映射,用于快速查找 |
||||
private var systemFunctionHandler: SystemFunctionHandler? |
private var toolMapping: [String: [String: Any]] = [:] |
||||
|
|
||||
// SSE会话 |
// URL会话 |
||||
private var sseSession: URLSession? |
private lazy var urlSession: URLSession = { |
||||
private var sseTask: URLSessionDataTask? |
let config = URLSessionConfiguration.default |
||||
|
config.timeoutIntervalForRequest = 30 |
||||
|
config.timeoutIntervalForResource = 30 |
||||
|
return URLSession(configuration: config) |
||||
|
}() |
||||
|
|
||||
/// 初始化MCPClient |
public init(serverId: String, serverUrl: String) { |
||||
/// - Parameter context: 可选的应用上下文 |
self.serverId = serverId |
||||
init(_ context: Any? = nil) { |
self.serverUrl = serverUrl |
||||
if let appContext = context { |
self.tag = "MCPSubClient[\(serverId)]" |
||||
systemFunctionHandler = SystemFunctionHandler(context: appContext) |
print("\(tag) 初始化,URL: \(serverUrl)") |
||||
|
|
||||
// 初始化语音功能 |
|
||||
initializeVoiceFunctions() |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
/// 初始化语音功能处理器 |
/// 连接到MCP服务器(通过SSE) |
||||
private func initializeVoiceFunctions() { |
/// - Returns: 是否连接成功 |
||||
|
public func connect() async -> Bool { |
||||
do { |
do { |
||||
// 使用SystemFunctionHandler注册所有功能 |
print("\(tag) 开始连接服务器: \(serverUrl)") |
||||
systemFunctionHandler?.registerAllFunctions(client: self) |
|
||||
|
// 这里应该实现实际的SSE连接逻辑 |
||||
|
// 由于iOS没有直接的SSE库,我们可以使用URLSession来模拟 |
||||
|
// 或者使用第三方库如EventSource |
||||
|
|
||||
print("\(TAG) 语音功能初始化完成") |
// 模拟连接成功并获取工具列表 |
||||
|
await processToolsList() |
||||
|
|
||||
|
isConnectedFlag = true |
||||
|
print("\(tag) 连接成功,可用工具: \(tools.count)个") |
||||
|
return true |
||||
} catch { |
} catch { |
||||
print("\(TAG) 语音功能初始化失败: \(error.localizedDescription)") |
print("\(tag) 连接失败: \(error.localizedDescription)") |
||||
|
return false |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
/// 注册本地函数 |
/// 处理工具列表 |
||||
/// - Parameters: |
private func processToolsList() async { |
||||
/// - name: 函数名称 |
// 这里应该从MCP服务器获取实际的工具列表 |
||||
/// - description: 函数描述 |
// 目前先模拟一些基本工具 |
||||
/// - parameters: 函数参数定义 |
tools = [] |
||||
/// - handler: 函数处理器 |
|
||||
/// - Returns: 是否注册成功 |
// 更新工具映射 |
||||
func registerLocalFunction(name: String, description: String, parameters: [String: Any], handler: FunctionHandler) -> Bool { |
toolMapping.removeAll() |
||||
do { |
for tool in tools { |
||||
// 存储函数处理器 |
if let name = tool["name"] as? String { |
||||
localFunctions[name] = handler |
toolMapping[name] = tool |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 更新工具Map列表 |
||||
|
updateToolMaps() |
||||
|
|
||||
|
print("\(tag) 成功处理 \(tools.count) 个工具") |
||||
|
} |
||||
|
|
||||
|
/// 更新工具Map列表,用于OpenAI请求 |
||||
|
/// 将MCP工具转换为OpenAI函数调用格式 |
||||
|
private func updateToolMaps() { |
||||
|
toolMaps.removeAll() |
||||
|
|
||||
|
for tool in tools { |
||||
|
guard let name = tool["name"] as? String, |
||||
|
let description = tool["description"] as? String else { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
// 创建函数定义 |
let functionMap: [String: Any] = [ |
||||
let functionDef: [String: Any] = [ |
|
||||
"name": name, |
"name": name, |
||||
"description": description, |
"description": description, |
||||
"parameters": parameters |
"parameters": [ |
||||
|
"type": "object", |
||||
|
"properties": [:], |
||||
|
"required": [] |
||||
|
] |
||||
] |
] |
||||
|
|
||||
// 添加到工具定义列表 |
toolMaps.append([ |
||||
let toolDef: [String: Any] = [ |
|
||||
"type": "function", |
"type": "function", |
||||
"function": functionDef |
"function": functionMap |
||||
] |
]) |
||||
|
|
||||
// 更新或添加工具定义 |
|
||||
if let index = toolDefinitions.firstIndex(where: { |
|
||||
guard let function = $0["function"] as? [String: Any], |
|
||||
let functionName = function["name"] as? String else { |
|
||||
return false |
|
||||
} |
|
||||
return functionName == name |
|
||||
}) { |
|
||||
toolDefinitions[index] = toolDef |
|
||||
} else { |
|
||||
toolDefinitions.append(toolDef) |
|
||||
} |
|
||||
|
|
||||
print("\(TAG) 成功注册本地函数: \(name)") |
|
||||
return true |
|
||||
} catch { |
|
||||
print("\(TAG) 注册函数失败: \(error.localizedDescription)") |
|
||||
return false |
|
||||
} |
} |
||||
|
|
||||
|
print("\(tag) 更新工具映射完成,共 \(toolMaps.count) 个工具") |
||||
} |
} |
||||
|
|
||||
/// 连接到MCP服务器(通过SSE) |
/// 获取工具Map列表,用于OpenAI请求 |
||||
/// - Parameter mcpServer: 服务器URL |
public func getToolMaps() -> [[String: Any]] { |
||||
/// - Returns: 是否连接成功 |
return toolMaps |
||||
func connectToSSE(_ mcpServer: String) -> Bool { |
} |
||||
// 关闭现有连接 |
|
||||
closeConnection() |
/// 获取可用的工具列表 |
||||
|
public func getTools() -> [[String: Any]] { |
||||
// 如果URL为空,返回失败 |
return tools |
||||
if mcpServer.isEmpty { |
} |
||||
print("\(TAG) MCP服务器URL为空") |
|
||||
return false |
/// 检查是否包含指定工具 |
||||
|
public func containsTool(name: String) -> Bool { |
||||
|
return toolMapping.keys.contains(name) |
||||
|
} |
||||
|
|
||||
|
/// 调用工具 |
||||
|
public func callTool(name: String, arguments: [String: Any]) async throws -> [String: Any] { |
||||
|
guard isConnectedFlag else { |
||||
|
print("\(tag) 未连接到服务器") |
||||
|
return ["context": "未连接到MCP服务器"] |
||||
} |
} |
||||
|
|
||||
// 解析服务器URL |
guard containsTool(name: name) else { |
||||
guard let serverURL = URL(string: mcpServer) else { |
print("\(tag) 此客户端不包含工具: \(name)") |
||||
print("\(TAG) 无效的MCP服务器URL: \(mcpServer)") |
throw OpenAIException("工具不存在: \(name)") |
||||
return false |
|
||||
} |
} |
||||
|
|
||||
print("\(TAG) 正在连接到MCP服务器: \(mcpServer)") |
// 这里应该实现实际的工具调用逻辑 |
||||
|
// 目前先返回模拟结果 |
||||
|
return [ |
||||
|
"name": name, |
||||
|
"context": "工具调用结果: \(name)" |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
/// 检查是否已连接 |
||||
|
public func isConnected() -> Bool { |
||||
|
return isConnectedFlag |
||||
|
} |
||||
|
|
||||
|
/// 关闭连接 |
||||
|
public func close() { |
||||
|
isConnectedFlag = false |
||||
|
print("\(tag) 已关闭MCP服务器连接") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// MCP客户端,用于连接Model Context Protocol服务器和管理本地函数 |
||||
|
public class MCPClient { |
||||
|
private let tag = "MCPClient" |
||||
|
|
||||
|
// 本地函数Map,函数名 -> 处理器 |
||||
|
private var localFunctions: [String: FunctionHandler] = [:] |
||||
|
|
||||
|
// 本地函数定义Map,函数名 -> 定义 |
||||
|
private var localFunctionDefs: [String: String] = [:] |
||||
|
|
||||
|
// 子客户端列表,每个连接一个MCP服务器 |
||||
|
private var subClients: [String: MCPSubClient] = [:] |
||||
|
|
||||
|
// 是否已连接 |
||||
|
private var isConnectedFlag = false |
||||
|
|
||||
|
// 系统功能处理器 |
||||
|
private var systemFunctionHandler: SystemFunctionHandler? |
||||
|
|
||||
|
public init() { |
||||
|
// 初始化系统功能处理器 |
||||
|
systemFunctionHandler = SystemFunctionHandler() |
||||
|
initializeVoiceFunctions() |
||||
|
} |
||||
|
|
||||
|
/// 初始化语音功能处理器 |
||||
|
private func initializeVoiceFunctions() { |
||||
|
do { |
||||
|
// 使用SystemFunctionHandler注册所有功能 |
||||
|
systemFunctionHandler?.registerAllFunctions(client: self) |
||||
|
print("\(tag) 语音功能初始化完成") |
||||
|
} catch { |
||||
|
print("\(tag) 语音功能初始化失败: \(error.localizedDescription)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 连接到MCP服务器(通过SSE) |
||||
|
/// 直接接收完整的JSON配置字符串 |
||||
|
/// - Parameter mcpConfigJson: 包含mcpServers字段的JSON配置字符串 |
||||
|
/// - Returns: 是否连接成功 |
||||
|
public func connectToSSE(mcpConfigJson: String) async throws -> Bool { |
||||
|
// 清除现有连接 |
||||
|
closeAllConnections() |
||||
|
|
||||
// 创建会话配置 |
var serverMap: [String: String] = [:] |
||||
let config = URLSessionConfiguration.default |
var configLoaded = false |
||||
config.timeoutIntervalForRequest = 30 |
|
||||
config.timeoutIntervalForResource = 60 |
|
||||
|
|
||||
// 创建会话 |
// 尝试解析传入的JSON配置 |
||||
let session = URLSession(configuration: config) |
if !mcpConfigJson.isEmpty { |
||||
self.sseSession = session |
do { |
||||
|
guard let data = mcpConfigJson.data(using: .utf8), |
||||
|
let config = try JSONSerialization.jsonObject(with: data) as? [String: Any], |
||||
|
let mcpServers = config["mcpServers"] as? [String: [String: String]] else { |
||||
|
throw OpenAIException("无效的MCP配置格式") |
||||
|
} |
||||
|
|
||||
|
// 从服务器配置中提取URL |
||||
|
for (serverId, serverConfig) in mcpServers { |
||||
|
if let url = serverConfig["url"], !url.isEmpty { |
||||
|
serverMap[serverId] = url |
||||
|
print("\(tag) 从配置中添加服务器 [\(serverId)]: \(url)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
configLoaded = !serverMap.isEmpty |
||||
|
} catch { |
||||
|
print("\(tag) 解析MCP配置失败: \(error.localizedDescription)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
// 创建请求 |
// 如果没有从配置中加载到服务器,使用默认配置 |
||||
var request = URLRequest(url: serverURL) |
if !configLoaded { |
||||
request.httpMethod = "GET" |
print("\(tag) 使用默认MCP服务器配置") |
||||
request.addValue("text/event-stream", forHTTPHeaderField: "Accept") |
// 可以在这里添加默认的服务器配置 |
||||
|
} |
||||
|
|
||||
// 创建并启动SSE任务 |
// 连接到所有服务器 |
||||
let task = session.dataTask(with: request) { [weak self] data, response, error in |
var successCount = 0 |
||||
guard let self = self else { return } |
for (serverId, serverUrl) in serverMap { |
||||
|
let subClient = MCPSubClient(serverId: serverId, serverUrl: serverUrl) |
||||
|
let connected = await subClient.connect() |
||||
|
|
||||
if let error = error { |
if connected { |
||||
print("\(self.TAG) MCP服务器连接失败: \(error.localizedDescription)") |
subClients[serverId] = subClient |
||||
self.isConnected = false |
successCount += 1 |
||||
return |
print("\(tag) 成功连接到服务器 [\(serverId)]") |
||||
} |
} else { |
||||
|
print("\(tag) 连接服务器失败 [\(serverId)]") |
||||
if let httpResponse = response as? HTTPURLResponse { |
|
||||
if httpResponse.statusCode == 200 { |
|
||||
print("\(self.TAG) 成功连接到MCP服务器") |
|
||||
self.isConnected = true |
|
||||
|
|
||||
// 处理服务器响应数据 |
|
||||
if let data = data, let responseString = String(data: data, encoding: .utf8) { |
|
||||
print("\(self.TAG) 收到初始响应: \(responseString)") |
|
||||
|
|
||||
// 在这里解析可用工具列表,暂时模拟 |
|
||||
// 实际情况下,需要实现SSE流解析 |
|
||||
} |
|
||||
} else { |
|
||||
print("\(self.TAG) MCP服务器连接失败,状态码: \(httpResponse.statusCode)") |
|
||||
self.isConnected = false |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
task.resume() |
isConnectedFlag = successCount > 0 |
||||
self.sseTask = task |
print("\(tag) MCP客户端连接完成,成功连接 \(successCount)/\(serverMap.count) 个服务器") |
||||
|
|
||||
// 在iOS中,连接是异步的,此处返回true表示连接已开始 |
return isConnectedFlag |
||||
// 实际连接状态通过isConnected属性检查 |
|
||||
return true |
|
||||
} |
} |
||||
|
|
||||
/// 检查是否已连接 |
/// 关闭所有连接 |
||||
/// - Returns: 是否已连接 |
private func closeAllConnections() { |
||||
func checkIsConnected() -> Bool { |
for (_, subClient) in subClients { |
||||
return isConnected |
subClient.close() |
||||
} |
} |
||||
|
subClients.removeAll() |
||||
/// 关闭连接 |
isConnectedFlag = false |
||||
func closeConnection() { |
|
||||
sseTask?.cancel() |
|
||||
sseTask = nil |
|
||||
isConnected = false |
|
||||
} |
} |
||||
|
|
||||
/// 获取工具定义列表,用于OpenAI请求 |
/// 获取工具Map列表,用于OpenAI请求 |
||||
/// - Returns: 工具定义列表 |
public func getToolMaps() -> [[String: Any]] { |
||||
func getToolMaps() -> [[String: Any]] { |
var allToolMaps: [[String: Any]] = [] |
||||
return toolDefinitions |
|
||||
|
// 添加所有子客户端的工具 |
||||
|
for (_, subClient) in subClients { |
||||
|
allToolMaps.append(contentsOf: subClient.getToolMaps()) |
||||
|
} |
||||
|
|
||||
|
// 添加本地函数 |
||||
|
for (name, _) in localFunctions { |
||||
|
if let def = localFunctionDefs[name], |
||||
|
let data = def.data(using: .utf8), |
||||
|
let functionDef = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { |
||||
|
allToolMaps.append([ |
||||
|
"type": "function", |
||||
|
"function": functionDef |
||||
|
]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return allToolMaps |
||||
} |
} |
||||
|
|
||||
/// 解析JSON参数 |
/// 注册本地函数 |
||||
/// - Parameter argumentsJson: JSON字符串 |
public func registerLocalFunction(name: String, description: String, parameters: [String: Any], handler: FunctionHandler) -> Bool { |
||||
/// - Returns: 解析后的参数字典 |
do { |
||||
func parseJsonArguments(_ argumentsJson: String) -> [String: Any] { |
localFunctions[name] = handler |
||||
guard !argumentsJson.isEmpty, |
|
||||
let data = argumentsJson.data(using: .utf8), |
let functionDef: [String: Any] = [ |
||||
let parsedArgs = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { |
"name": name, |
||||
return [:] |
"description": description, |
||||
|
"parameters": parameters |
||||
|
] |
||||
|
|
||||
|
let jsonData = try JSONSerialization.data(withJSONObject: functionDef) |
||||
|
let jsonString = String(data: jsonData, encoding: .utf8) ?? "" |
||||
|
localFunctionDefs[name] = jsonString |
||||
|
|
||||
|
print("\(tag) 成功注册本地函数: \(name)") |
||||
|
return true |
||||
|
} catch { |
||||
|
print("\(tag) 注册本地函数失败: \(error.localizedDescription)") |
||||
|
return false |
||||
} |
} |
||||
return parsedArgs |
|
||||
} |
} |
||||
|
|
||||
/// 调用工具 |
/// 调用工具 |
||||
/// - Parameters: |
public func callTool(name: String, arguments: [String: Any]) async throws -> [String: Any] { |
||||
/// - name: 工具名称 |
// 首先检查是否是本地函数 |
||||
/// - arguments: 工具参数 |
if let handler = localFunctions[name] { |
||||
/// - Returns: 工具调用结果 |
do { |
||||
func callTool(name: String, arguments: [String: Any]) async -> [String: Any] { |
|
||||
do { |
|
||||
// 检查是否为本地函数 |
|
||||
if let handler = localFunctions[name] { |
|
||||
let result = try await handler.handle(arguments: arguments) |
let result = try await handler.handle(arguments: arguments) |
||||
return [ |
return ["context": result] |
||||
"name": name, |
} catch { |
||||
"context": result |
throw OpenAIException("本地函数调用失败: \(error.localizedDescription)") |
||||
] |
|
||||
} |
} |
||||
|
} |
||||
// 如果不是本地函数,尝试作为MCP工具调用 |
|
||||
if isConnected { |
// 然后检查MCP工具 |
||||
// 这里应该实现真正的MCP工具调用 |
for (_, subClient) in subClients { |
||||
// 暂时返回一个模拟结果 |
if subClient.containsTool(name: name) { |
||||
return [ |
return try await subClient.callTool(name: name, arguments: arguments) |
||||
"name": name, |
|
||||
"context": "模拟MCP工具调用结果: \(name)(\(arguments))" |
|
||||
] |
|
||||
} |
} |
||||
|
|
||||
return ["context": "无法处理工具调用:找不到工具 \(name) 或MCP未连接"] |
|
||||
} catch { |
|
||||
print("\(TAG) 调用工具失败: \(error.localizedDescription)") |
|
||||
return ["context": "工具调用失败: \(error.localizedDescription)"] |
|
||||
} |
} |
||||
|
|
||||
|
throw OpenAIException("未找到工具: \(name)") |
||||
|
} |
||||
|
|
||||
|
/// 解析JSON参数 |
||||
|
public func parseJsonArguments(_ argumentsJson: String) throws -> [String: Any] { |
||||
|
guard let data = argumentsJson.data(using: .utf8), |
||||
|
let arguments = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { |
||||
|
return [:] |
||||
|
} |
||||
|
return arguments |
||||
} |
} |
||||
|
|
||||
/// 关闭MCP客户端 |
/// 检查是否已连接 |
||||
func close() { |
public func isConnected() -> Bool { |
||||
closeConnection() |
return isConnectedFlag || !localFunctions.isEmpty |
||||
|
} |
||||
|
|
||||
|
/// 关闭连接 |
||||
|
public func close() { |
||||
|
closeAllConnections() |
||||
localFunctions.removeAll() |
localFunctions.removeAll() |
||||
toolDefinitions.removeAll() |
localFunctionDefs.removeAll() |
||||
|
print("\(tag) 已关闭MCP客户端") |
||||
} |
} |
||||
} |
} |
||||
File diff suppressed because it is too large
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@ |
|||||
#Tue May 20 17:39:11 IST 2025 |
|
||||
gradle.version=8.9 |
|
||||
@ -1 +0,0 @@ |
|||||
C7E6339E39539FBB82728828DAB8AC59D6F180C3E383F85B403CD9B85CA1DF6A |
|
||||
@ -1,92 +0,0 @@ |
|||||
# Opus Plugin for Flutter |
|
||||
|
|
||||
一个用于在 Flutter 中使用 Opus 音频编解码器的插件,支持 Android 和 iOS 平台。 |
|
||||
|
|
||||
## 功能 |
|
||||
|
|
||||
- Opus 编码:将 PCM 音频数据编码为 Opus 格式 |
|
||||
- Opus 解码:将 Opus 数据解码为 PCM 音频 |
|
||||
- 支持灵活设置编码参数:采样率、声道数、应用类型、复杂度等 |
|
||||
- 兼容 Android 和 iOS 平台 |
|
||||
|
|
||||
## 使用方法 |
|
||||
|
|
||||
### 添加依赖 |
|
||||
|
|
||||
由于这是本地插件,在 `pubspec.yaml` 中添加: |
|
||||
|
|
||||
```yaml |
|
||||
dependencies: |
|
||||
opus: |
|
||||
path: local_plugins/opus |
|
||||
``` |
|
||||
|
|
||||
### 初始化编解码器 |
|
||||
|
|
||||
```dart |
|
||||
// 创建编码器 |
|
||||
await Opus.createEncoder( |
|
||||
sampleRate: 48000, // 采样率 (Hz) |
|
||||
channels: 2, // 声道数 (1=单声道, 2=立体声) |
|
||||
application: OpusApplicationType.audio, // 应用类型 |
|
||||
complexity: 10, // 复杂度 (0-10) |
|
||||
frameSize: 960, // 帧大小 |
|
||||
); |
|
||||
|
|
||||
// 创建解码器 |
|
||||
await Opus.createDecoder( |
|
||||
sampleRate: 48000, |
|
||||
channels: 2, |
|
||||
); |
|
||||
``` |
|
||||
|
|
||||
### 编码和解码 |
|
||||
|
|
||||
```dart |
|
||||
// 编码 PCM 数据 |
|
||||
Uint8List pcmData = ...; // 16位 PCM 音频数据 |
|
||||
Uint8List? opusData = await Opus.encodeData(pcmData); |
|
||||
|
|
||||
// 解码 Opus 数据 |
|
||||
Uint8List opusData = ...; // Opus 编码数据 |
|
||||
Uint8List? pcmData = await Opus.decodeData(opusData, frameSize: 960); |
|
||||
``` |
|
||||
|
|
||||
### 释放资源 |
|
||||
|
|
||||
```dart |
|
||||
// 释放编码器资源 |
|
||||
await Opus.disposeEncoder(); |
|
||||
|
|
||||
// 释放解码器资源 |
|
||||
await Opus.disposeDecoder(); |
|
||||
``` |
|
||||
|
|
||||
## 关于 Opus |
|
||||
|
|
||||
Opus 是一种开源、免版税的音频编解码器,专为通过互联网传输音频而设计。它可变比特率从 6 kbit/s 到 510 kbit/s,帧大小从 2.5 ms 到 60 ms,支持采样率从 8 kHz(窄带)到 48 kHz(全频带)。 |
|
||||
|
|
||||
Opus 特点: |
|
||||
- 低延迟 (5-20ms) |
|
||||
- 高音质 |
|
||||
- 灵活的比特率 |
|
||||
- 支持语音和音乐 |
|
||||
- 强大的丢包恢复能力 |
|
||||
|
|
||||
## 配置说明 |
|
||||
|
|
||||
### Android 配置 |
|
||||
|
|
||||
需要将 libopus.so 文件放在以下目录: |
|
||||
- android/src/main/jniLibs/armeabi-v7a/libopus.so |
|
||||
- android/src/main/jniLibs/arm64-v8a/libopus.so |
|
||||
|
|
||||
> 注意:若需要自行编译 Opus,可参考 https://opus-codec.org/ 官方指南。 |
|
||||
|
|
||||
### iOS 配置 |
|
||||
|
|
||||
需要将 opus.framework 或 libopus.a 放在 ios/Frameworks 目录下。 |
|
||||
|
|
||||
## 示例 |
|
||||
|
|
||||
查看 [example](example) 目录中的示例代码,了解如何使用此插件生成、编码和解码音频数据。 |
|
||||
@ -1,21 +0,0 @@ |
|||||
cmake_minimum_required(VERSION 3.4.1) |
|
||||
|
|
||||
# 设置opus源码路径,使用我们自己的头文件 |
|
||||
set(OPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/opus) |
|
||||
|
|
||||
# 添加opus头文件路径 |
|
||||
include_directories(${OPUS_DIR}/include) |
|
||||
|
|
||||
# 创建我们自己的opus库(桩实现) |
|
||||
add_library(opus SHARED |
|
||||
src/main/cpp/opus_stub.cpp) |
|
||||
|
|
||||
# 添加JNI库 |
|
||||
add_library(opus_jni SHARED |
|
||||
src/main/cpp/opus_jni.cpp) |
|
||||
|
|
||||
# 链接库 |
|
||||
target_link_libraries(opus_jni |
|
||||
android |
|
||||
log |
|
||||
opus) |
|
||||
@ -1,67 +0,0 @@ |
|||||
plugins { |
|
||||
// Android Library 插件 |
|
||||
id("com.android.library") |
|
||||
// Kotlin Android 插件 |
|
||||
id("org.jetbrains.kotlin.android") |
|
||||
} |
|
||||
|
|
||||
group = "com.yunqiinnovation.opus" |
|
||||
version = "1.0-SNAPSHOT" |
|
||||
|
|
||||
android { |
|
||||
namespace = "com.yunqiinnovation.opus" |
|
||||
|
|
||||
// 目标 SDK 版本 |
|
||||
compileSdk = 35 |
|
||||
|
|
||||
defaultConfig { |
|
||||
// 最低 SDK 版本 |
|
||||
minSdk = 21 |
|
||||
|
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" |
|
||||
|
|
||||
// 设置启用CMake构建 |
|
||||
externalNativeBuild { |
|
||||
cmake { |
|
||||
arguments += "-DANDROID_STL=c++_shared" |
|
||||
cppFlags += "-std=c++11" |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 设置支持的ABIs |
|
||||
ndk { |
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a") |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Java 语言级别兼容配置 |
|
||||
compileOptions { |
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8 |
|
||||
targetCompatibility = JavaVersion.VERSION_1_8 |
|
||||
} |
|
||||
|
|
||||
// Kotlin 语言级别 |
|
||||
kotlinOptions { |
|
||||
jvmTarget = "1.8" |
|
||||
} |
|
||||
|
|
||||
// CMake配置 |
|
||||
externalNativeBuild { |
|
||||
cmake { |
|
||||
path = file("CMakeLists.txt") |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 处理jniLibs |
|
||||
sourceSets { |
|
||||
getByName("main") { |
|
||||
jniLibs.srcDirs("src/main/jniLibs") |
|
||||
java.srcDir("src/main/kotlin") |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
dependencies { |
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10") |
|
||||
implementation("androidx.annotation:annotation:1.3.0") |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
Jean-Marc Valin (jmvalin@jmvalin.ca) |
|
||||
Koen Vos (koenvos74@gmail.com) |
|
||||
Timothy Terriberry (tterribe@xiph.org) |
|
||||
Karsten Vandborg Sorensen (karsten.vandborg.sorensen@skype.net) |
|
||||
Soren Skak Jensen (ssjensen@gn.com) |
|
||||
Gregory Maxwell (greg@xiph.org) |
|
||||
@ -1,744 +0,0 @@ |
|||||
cmake_minimum_required(VERSION 3.16) |
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") |
|
||||
|
|
||||
include(OpusPackageVersion) |
|
||||
get_package_version(PACKAGE_VERSION PROJECT_VERSION) |
|
||||
|
|
||||
project(Opus LANGUAGES C VERSION ${PROJECT_VERSION}) |
|
||||
|
|
||||
include(OpusFunctions) |
|
||||
include(OpusBuildtype) |
|
||||
include(OpusConfig) |
|
||||
include(OpusSources) |
|
||||
include(GNUInstallDirs) |
|
||||
include(CMakeDependentOption) |
|
||||
include(FeatureSummary) |
|
||||
|
|
||||
set(OPUS_BUILD_SHARED_LIBRARY_HELP_STR "build shared library.") |
|
||||
option(OPUS_BUILD_SHARED_LIBRARY ${OPUS_BUILD_SHARED_LIBRARY_HELP_STR} OFF) |
|
||||
if(OPUS_BUILD_SHARED_LIBRARY OR BUILD_SHARED_LIBS OR OPUS_BUILD_FRAMEWORK) |
|
||||
# Global flag to cause add_library() to create shared libraries if on. |
|
||||
set(BUILD_SHARED_LIBS ON) |
|
||||
set(OPUS_BUILD_SHARED_LIBRARY ON) |
|
||||
endif() |
|
||||
add_feature_info(OPUS_BUILD_SHARED_LIBRARY OPUS_BUILD_SHARED_LIBRARY ${OPUS_BUILD_SHARED_LIBRARY_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_BUILD_TESTING_HELP_STR "build tests.") |
|
||||
option(OPUS_BUILD_TESTING ${OPUS_BUILD_TESTING_HELP_STR} OFF) |
|
||||
if(OPUS_BUILD_TESTING OR BUILD_TESTING) |
|
||||
set(OPUS_BUILD_TESTING ON) |
|
||||
set(BUILD_TESTING ON) |
|
||||
endif() |
|
||||
add_feature_info(OPUS_BUILD_TESTING OPUS_BUILD_TESTING ${OPUS_BUILD_TESTING_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_CUSTOM_MODES_HELP_STR "enable non-Opus modes, e.g. 44.1 kHz & 2^n frames.") |
|
||||
option(OPUS_CUSTOM_MODES ${OPUS_CUSTOM_MODES_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_CUSTOM_MODES OPUS_CUSTOM_MODES ${OPUS_CUSTOM_MODES_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_BUILD_PROGRAMS_HELP_STR "build programs.") |
|
||||
option(OPUS_BUILD_PROGRAMS ${OPUS_BUILD_PROGRAMS_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_BUILD_PROGRAMS OPUS_BUILD_PROGRAMS ${OPUS_BUILD_PROGRAMS_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_DISABLE_INTRINSICS_HELP_STR "disable all intrinsics optimizations.") |
|
||||
option(OPUS_DISABLE_INTRINSICS ${OPUS_DISABLE_INTRINSICS_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_DISABLE_INTRINSICS OPUS_DISABLE_INTRINSICS ${OPUS_DISABLE_INTRINSICS_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_FIXED_POINT_HELP_STR "compile as fixed-point (for machines without a fast enough FPU).") |
|
||||
option(OPUS_FIXED_POINT ${OPUS_FIXED_POINT_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_FIXED_POINT OPUS_FIXED_POINT ${OPUS_FIXED_POINT_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_ENABLE_FLOAT_API_HELP_STR "compile with the floating point API (for machines with float library).") |
|
||||
option(OPUS_ENABLE_FLOAT_API ${OPUS_ENABLE_FLOAT_API_HELP_STR} ON) |
|
||||
add_feature_info(OPUS_ENABLE_FLOAT_API OPUS_ENABLE_FLOAT_API ${OPUS_ENABLE_FLOAT_API_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_FLOAT_APPROX_HELP_STR "enable floating point approximations (Ensure your platform supports IEEE 754 before enabling).") |
|
||||
option(OPUS_FLOAT_APPROX ${OPUS_FLOAT_APPROX_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_FLOAT_APPROX OPUS_FLOAT_APPROX ${OPUS_FLOAT_APPROX_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_ASSERTIONS_HELP_STR "additional software error checking.") |
|
||||
option(OPUS_ASSERTIONS ${OPUS_ASSERTIONS_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_ASSERTIONS OPUS_ASSERTIONS ${OPUS_ASSERTIONS_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_HARDENING_HELP_STR "run-time checks that are cheap and safe for use in production.") |
|
||||
option(OPUS_HARDENING ${OPUS_HARDENING_HELP_STR} ON) |
|
||||
add_feature_info(OPUS_HARDENING OPUS_HARDENING ${OPUS_HARDENING_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_FUZZING_HELP_STR "causes the encoder to make random decisions (do not use in production).") |
|
||||
option(OPUS_FUZZING ${OPUS_FUZZING_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_FUZZING OPUS_FUZZING ${OPUS_FUZZING_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_CHECK_ASM_HELP_STR "enable bit-exactness checks between optimized and c implementations.") |
|
||||
option(OPUS_CHECK_ASM ${OPUS_CHECK_ASM_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_CHECK_ASM OPUS_CHECK_ASM ${OPUS_CHECK_ASM_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_DNN_FLOAT_DEBUG_HELP_STR "Run DNN computations as float for debugging purposes.") |
|
||||
option(OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_DNN_FLOAT_DEBUG OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR "install pkg-config module.") |
|
||||
option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ON) |
|
||||
add_feature_info(OPUS_INSTALL_PKG_CONFIG_MODULE OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR "install CMake package config module.") |
|
||||
option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ON) |
|
||||
add_feature_info(OPUS_INSTALL_CMAKE_CONFIG_MODULE OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_DRED_HELP_STR "enable DRED.") |
|
||||
option(OPUS_DRED ${OPUS_DRED_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_DRED OPUS_DRED ${OPUS_DRED_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_OSCE_HELP_STR "enable OSCE.") |
|
||||
option(OPUS_OSCE ${OPUS_OSCE_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_OSCE OPUS_OSCE ${OPUS_OSCE_HELP_STR}) |
|
||||
|
|
||||
if(APPLE) |
|
||||
set(OPUS_BUILD_FRAMEWORK_HELP_STR "build Framework bundle for Apple systems.") |
|
||||
option(OPUS_BUILD_FRAMEWORK ${OPUS_BUILD_FRAMEWORK_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_BUILD_FRAMEWORK OPUS_BUILD_FRAMEWORK ${OPUS_BUILD_FRAMEWORK_HELP_STR}) |
|
||||
endif() |
|
||||
|
|
||||
if(MSVC) |
|
||||
set(OPUS_STATIC_RUNTIME_HELP_STR "build with static runtime library.") |
|
||||
option(OPUS_STATIC_RUNTIME ${OPUS_STATIC_RUNTIME_HELP_STR} OFF) |
|
||||
add_feature_info(OPUS_STATIC_RUNTIME OPUS_STATIC_RUNTIME ${OPUS_STATIC_RUNTIME_HELP_STR}) |
|
||||
endif() |
|
||||
|
|
||||
set(OPUS_FIXED_POINT_DEBUG_HELP_STR "debug fixed-point implementation.") |
|
||||
cmake_dependent_option(OPUS_FIXED_POINT_DEBUG |
|
||||
${OPUS_FIXED_POINT_DEBUG_HELP_STR} |
|
||||
ON |
|
||||
"OPUS_FIXED_POINT; OPUS_FIXED_POINT_DEBUG" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_FIXED_POINT_DEBUG OPUS_FIXED_POINT_DEBUG ${OPUS_FIXED_POINT_DEBUG_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_VAR_ARRAYS_HELP_STR "use variable length arrays for stack arrays.") |
|
||||
cmake_dependent_option(OPUS_VAR_ARRAYS |
|
||||
${OPUS_VAR_ARRAYS_HELP_STR} |
|
||||
ON |
|
||||
"VLA_SUPPORTED; NOT OPUS_USE_ALLOCA; NOT OPUS_NONTHREADSAFE_PSEUDOSTACK" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_VAR_ARRAYS OPUS_VAR_ARRAYS ${OPUS_VAR_ARRAYS_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_USE_ALLOCA_HELP_STR "use alloca for stack arrays (on non-C99 compilers).") |
|
||||
cmake_dependent_option(OPUS_USE_ALLOCA |
|
||||
${OPUS_USE_ALLOCA_HELP_STR} |
|
||||
ON |
|
||||
"USE_ALLOCA_SUPPORTED; NOT OPUS_VAR_ARRAYS; NOT OPUS_NONTHREADSAFE_PSEUDOSTACK" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_USE_ALLOCA OPUS_USE_ALLOCA ${OPUS_USE_ALLOCA_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_NONTHREADSAFE_PSEUDOSTACK_HELP_STR "use a non threadsafe pseudostack when neither variable length arrays or alloca is supported.") |
|
||||
cmake_dependent_option(OPUS_NONTHREADSAFE_PSEUDOSTACK |
|
||||
${OPUS_NONTHREADSAFE_PSEUDOSTACK_HELP_STR} |
|
||||
ON |
|
||||
"NOT OPUS_VAR_ARRAYS; NOT OPUS_USE_ALLOCA" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_NONTHREADSAFE_PSEUDOSTACK OPUS_NONTHREADSAFE_PSEUDOSTACK ${OPUS_NONTHREADSAFE_PSEUDOSTACK_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_FAST_MATH_HELP_STR "enable fast math (unsupported and discouraged use, as code is not well tested with this build option).") |
|
||||
cmake_dependent_option(OPUS_FAST_MATH |
|
||||
${OPUS_FAST_MATH_HELP_STR} |
|
||||
ON |
|
||||
"OPUS_FLOAT_APPROX; OPUS_FAST_MATH; FAST_MATH_SUPPORTED" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_FAST_MATH OPUS_FAST_MATH ${OPUS_FAST_MATH_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_STACK_PROTECTOR_HELP_STR "use stack protection.") |
|
||||
cmake_dependent_option(OPUS_STACK_PROTECTOR |
|
||||
${OPUS_STACK_PROTECTOR_HELP_STR} |
|
||||
ON |
|
||||
"STACK_PROTECTOR_SUPPORTED" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_STACK_PROTECTOR OPUS_STACK_PROTECTOR ${OPUS_STACK_PROTECTOR_HELP_STR}) |
|
||||
|
|
||||
if(NOT MSVC) |
|
||||
set(OPUS_FORTIFY_SOURCE_HELP_STR "add protection against buffer overflows.") |
|
||||
cmake_dependent_option(OPUS_FORTIFY_SOURCE |
|
||||
${OPUS_FORTIFY_SOURCE_HELP_STR} |
|
||||
ON |
|
||||
"FORTIFY_SOURCE_SUPPORTED" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_FORTIFY_SOURCE OPUS_FORTIFY_SOURCE ${OPUS_FORTIFY_SOURCE_HELP_STR}) |
|
||||
endif() |
|
||||
|
|
||||
if(MINGW AND (OPUS_FORTIFY_SOURCE OR OPUS_STACK_PROTECTOR)) |
|
||||
# ssp lib is needed for security features for MINGW |
|
||||
list(APPEND OPUS_REQUIRED_LIBRARIES ssp) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_CPU_X86 OR OPUS_CPU_X64) |
|
||||
set(OPUS_X86_MAY_HAVE_SSE_HELP_STR "does runtime check for SSE1 support.") |
|
||||
cmake_dependent_option(OPUS_X86_MAY_HAVE_SSE |
|
||||
${OPUS_X86_MAY_HAVE_SSE_HELP_STR} |
|
||||
ON |
|
||||
"SSE1_SUPPORTED; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_MAY_HAVE_SSE OPUS_X86_MAY_HAVE_SSE ${OPUS_X86_MAY_HAVE_SSE_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_X86_MAY_HAVE_SSE2_HELP_STR "does runtime check for SSE2 support.") |
|
||||
cmake_dependent_option(OPUS_X86_MAY_HAVE_SSE2 |
|
||||
${OPUS_X86_MAY_HAVE_SSE2_HELP_STR} |
|
||||
ON |
|
||||
"SSE2_SUPPORTED; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_MAY_HAVE_SSE2 OPUS_X86_MAY_HAVE_SSE2 ${OPUS_X86_MAY_HAVE_SSE2_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_X86_MAY_HAVE_SSE4_1_HELP_STR "does runtime check for SSE4.1 support.") |
|
||||
cmake_dependent_option(OPUS_X86_MAY_HAVE_SSE4_1 |
|
||||
${OPUS_X86_MAY_HAVE_SSE4_1_HELP_STR} |
|
||||
ON |
|
||||
"SSE4_1_SUPPORTED; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_MAY_HAVE_SSE4_1 OPUS_X86_MAY_HAVE_SSE4_1 ${OPUS_X86_MAY_HAVE_SSE4_1_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_X86_MAY_HAVE_AVX2_HELP_STR "does runtime check for AVX FMA AVX2 support.") |
|
||||
cmake_dependent_option(OPUS_X86_MAY_HAVE_AVX2 |
|
||||
${OPUS_X86_MAY_HAVE_AVX2_HELP_STR} |
|
||||
ON |
|
||||
"AVX2_SUPPORTED; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_MAY_HAVE_AVX2 OPUS_X86_MAY_HAVE_AVX2 ${OPUS_X86_MAY_HAVE_AVX2_HELP_STR}) |
|
||||
|
|
||||
# PRESUME depends on MAY HAVE, but PRESUME will override runtime detection |
|
||||
set(OPUS_X86_PRESUME_SSE_HELP_STR "assume target CPU has SSE1 support (override runtime check).") |
|
||||
set(OPUS_X86_PRESUME_SSE2_HELP_STR "assume target CPU has SSE2 support (override runtime check).") |
|
||||
if(OPUS_CPU_X64) # Assume x86_64 has up to SSE2 support |
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_SSE |
|
||||
${OPUS_X86_PRESUME_SSE_HELP_STR} |
|
||||
ON |
|
||||
"OPUS_X86_MAY_HAVE_SSE; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
|
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_SSE2 |
|
||||
${OPUS_X86_PRESUME_SSE2_HELP_STR} |
|
||||
ON |
|
||||
"OPUS_X86_MAY_HAVE_SSE2; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
else() |
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_SSE |
|
||||
${OPUS_X86_PRESUME_SSE_HELP_STR} |
|
||||
OFF |
|
||||
"OPUS_X86_MAY_HAVE_SSE; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
|
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_SSE2 |
|
||||
${OPUS_X86_PRESUME_SSE2_HELP_STR} |
|
||||
OFF |
|
||||
"OPUS_X86_MAY_HAVE_SSE2; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
endif() |
|
||||
add_feature_info(OPUS_X86_PRESUME_SSE OPUS_X86_PRESUME_SSE ${OPUS_X86_PRESUME_SSE_HELP_STR}) |
|
||||
add_feature_info(OPUS_X86_PRESUME_SSE2 OPUS_X86_PRESUME_SSE2 ${OPUS_X86_PRESUME_SSE2_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_X86_PRESUME_SSE4_1_HELP_STR "assume target CPU has SSE4.1 support (override runtime check).") |
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_SSE4_1 |
|
||||
${OPUS_X86_PRESUME_SSE4_1_HELP_STR} |
|
||||
OFF |
|
||||
"OPUS_X86_MAY_HAVE_SSE4_1; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_PRESUME_SSE4_1 OPUS_X86_PRESUME_SSE4_1 ${OPUS_X86_PRESUME_SSE4_1_HELP_STR}) |
|
||||
|
|
||||
set(OPUS_X86_PRESUME_AVX2_HELP_STR "assume target CPU has AVX FMA AVX2 support (override runtime check).") |
|
||||
cmake_dependent_option(OPUS_X86_PRESUME_AVX2 |
|
||||
${OPUS_X86_PRESUME_AVX2_HELP_STR} |
|
||||
OFF |
|
||||
"OPUS_X86_MAY_HAVE_AVX2; NOT OPUS_DISABLE_INTRINSICS" |
|
||||
OFF) |
|
||||
add_feature_info(OPUS_X86_PRESUME_AVX2 OPUS_X86_PRESUME_AVX2 ${OPUS_X86_PRESUME_AVX2_HELP_STR}) |
|
||||
endif() |
|
||||
|
|
||||
feature_summary(WHAT ALL) |
|
||||
|
|
||||
set_package_properties(Git |
|
||||
PROPERTIES |
|
||||
TYPE |
|
||||
REQUIRED |
|
||||
DESCRIPTION |
|
||||
"fast, scalable, distributed revision control system" |
|
||||
URL |
|
||||
"https://git-scm.com/" |
|
||||
PURPOSE |
|
||||
"required to set up package version") |
|
||||
|
|
||||
set(Opus_PUBLIC_HEADER |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/opus.h |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/opus_defines.h |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/opus_multistream.h |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/opus_projection.h |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/opus_types.h) |
|
||||
|
|
||||
if(OPUS_CUSTOM_MODES) |
|
||||
list(APPEND Opus_PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include/opus_custom.h) |
|
||||
endif() |
|
||||
|
|
||||
if(MSVC) |
|
||||
if(OPUS_STATIC_RUNTIME) |
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") |
|
||||
else() |
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL") |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
add_library(opus ${opus_headers} ${opus_sources} ${opus_sources_float} ${Opus_PUBLIC_HEADER}) |
|
||||
add_library(Opus::opus ALIAS opus) |
|
||||
|
|
||||
get_library_version(OPUS_LIBRARY_VERSION OPUS_LIBRARY_VERSION_MAJOR) |
|
||||
message(DEBUG "Opus library version: ${OPUS_LIBRARY_VERSION}") |
|
||||
|
|
||||
set_target_properties(opus |
|
||||
PROPERTIES SOVERSION |
|
||||
${OPUS_LIBRARY_VERSION_MAJOR} |
|
||||
VERSION |
|
||||
${OPUS_LIBRARY_VERSION} |
|
||||
PUBLIC_HEADER |
|
||||
"${Opus_PUBLIC_HEADER}") |
|
||||
|
|
||||
target_include_directories( |
|
||||
opus |
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> |
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> |
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/opus> |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} |
|
||||
${CMAKE_CURRENT_SOURCE_DIR} |
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dnn |
|
||||
celt |
|
||||
silk) |
|
||||
|
|
||||
target_link_libraries(opus PRIVATE ${OPUS_REQUIRED_LIBRARIES}) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_BUILD) |
|
||||
|
|
||||
if(OPUS_FIXED_POINT_DEBUG) |
|
||||
target_compile_definitions(opus PRIVATE FIXED_DEBUG) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_FORTIFY_SOURCE AND NOT MSVC) |
|
||||
target_compile_definitions(opus PRIVATE |
|
||||
$<$<NOT:$<CONFIG:debug>>:_FORTIFY_SOURCE=2>) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_FLOAT_APPROX) |
|
||||
target_compile_definitions(opus PRIVATE FLOAT_APPROX) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_ASSERTIONS) |
|
||||
target_compile_definitions(opus PRIVATE ENABLE_ASSERTIONS) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_HARDENING) |
|
||||
target_compile_definitions(opus PRIVATE ENABLE_HARDENING) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_FUZZING) |
|
||||
target_compile_definitions(opus PRIVATE FUZZING) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_CHECK_ASM) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_CHECK_ASM) |
|
||||
endif() |
|
||||
|
|
||||
if(NOT OPUS_DNN_FLOAT_DEBUG) |
|
||||
target_compile_definitions(opus PRIVATE DISABLE_DEBUG_FLOAT) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_VAR_ARRAYS) |
|
||||
target_compile_definitions(opus PRIVATE VAR_ARRAYS) |
|
||||
elseif(OPUS_USE_ALLOCA) |
|
||||
target_compile_definitions(opus PRIVATE USE_ALLOCA) |
|
||||
elseif(OPUS_NONTHREADSAFE_PSEUDOSTACK) |
|
||||
target_compile_definitions(opus PRIVATE NONTHREADSAFE_PSEUDOSTACK) |
|
||||
else() |
|
||||
message(ERROR "Need to set a define for stack allocation") |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_CUSTOM_MODES) |
|
||||
target_compile_definitions(opus PRIVATE CUSTOM_MODES) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_FAST_MATH) |
|
||||
if(MSVC) |
|
||||
target_compile_options(opus PRIVATE /fp:fast) |
|
||||
else() |
|
||||
target_compile_options(opus PRIVATE -ffast-math) |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_STACK_PROTECTOR) |
|
||||
if(MSVC) |
|
||||
target_compile_options(opus PRIVATE /GS) |
|
||||
else() |
|
||||
target_compile_options(opus PRIVATE -fstack-protector-strong) |
|
||||
endif() |
|
||||
elseif(STACK_PROTECTOR_DISABLED_SUPPORTED) |
|
||||
target_compile_options(opus PRIVATE /GS-) |
|
||||
endif() |
|
||||
|
|
||||
if(BUILD_SHARED_LIBS) |
|
||||
if(WIN32) |
|
||||
target_compile_definitions(opus PRIVATE DLL_EXPORT) |
|
||||
elseif(HIDDEN_VISIBILITY_SUPPORTED) |
|
||||
set_target_properties(opus PROPERTIES C_VISIBILITY_PRESET hidden) |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
add_sources_group(opus silk ${silk_headers} ${silk_sources}) |
|
||||
add_sources_group(opus celt ${celt_headers} ${celt_sources}) |
|
||||
|
|
||||
if(OPUS_FIXED_POINT) |
|
||||
add_sources_group(opus silk ${silk_sources_fixed}) |
|
||||
target_include_directories(opus PRIVATE silk/fixed) |
|
||||
target_compile_definitions(opus PRIVATE FIXED_POINT=1) |
|
||||
else() |
|
||||
add_sources_group(opus silk ${silk_sources_float}) |
|
||||
target_include_directories(opus PRIVATE silk/float) |
|
||||
endif() |
|
||||
|
|
||||
if(NOT OPUS_ENABLE_FLOAT_API) |
|
||||
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API) |
|
||||
endif() |
|
||||
|
|
||||
if (OPUS_DEEP_PLC OR OPUS_DRED OR OPUS_OSCE) |
|
||||
add_sources_group(opus lpcnet ${deep_plc_headers} ${deep_plc_sources}) |
|
||||
set(OPUS_DNN TRUE) |
|
||||
else() |
|
||||
set(OPUS_DNN FALSE) |
|
||||
endif() |
|
||||
|
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${deep_plc_headers} ${deep_plc_sources}) |
|
||||
target_compile_definitions(opus PRIVATE ENABLE_DEEP_PLC) |
|
||||
endif() |
|
||||
|
|
||||
if (OPUS_DRED) |
|
||||
add_sources_group(opus lpcnet ${dred_headers} ${dred_sources}) |
|
||||
target_compile_definitions(opus PRIVATE ENABLE_DRED) |
|
||||
endif() |
|
||||
|
|
||||
if (OPUS_OSCE) |
|
||||
add_sources_group(opus lpcnet ${osce_headers} ${osce_sources}) |
|
||||
target_compile_definitions(opus PRIVATE ENABLE_OSCE) |
|
||||
endif() |
|
||||
|
|
||||
if(NOT OPUS_DISABLE_INTRINSICS) |
|
||||
if(((OPUS_X86_MAY_HAVE_SSE AND NOT OPUS_X86_PRESUME_SSE) OR |
|
||||
(OPUS_X86_MAY_HAVE_SSE2 AND NOT OPUS_X86_PRESUME_SSE2) OR |
|
||||
(OPUS_X86_MAY_HAVE_SSE4_1 AND NOT OPUS_X86_PRESUME_SSE4_1) OR |
|
||||
(OPUS_X86_MAY_HAVE_AVX2 AND NOT OPUS_X86_PRESUME_AVX2)) AND |
|
||||
RUNTIME_CPU_CAPABILITY_DETECTION) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_HAVE_RTCD) |
|
||||
if(NOT MSVC) |
|
||||
if(CPU_INFO_BY_ASM_SUPPORTED) |
|
||||
target_compile_definitions(opus PRIVATE CPU_INFO_BY_ASM) |
|
||||
elseif(CPU_INFO_BY_C_SUPPORTED) |
|
||||
target_compile_definitions(opus PRIVATE CPU_INFO_BY_C) |
|
||||
else() |
|
||||
message(ERROR "Runtime cpu capability detection is enabled while CPU_INFO is not supported") |
|
||||
endif() |
|
||||
endif() |
|
||||
add_sources_group(opus celt ${celt_sources_x86_rtcd}) |
|
||||
add_sources_group(opus silk ${silk_sources_x86_rtcd}) |
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${dnn_sources_x86_rtcd}) |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(SSE1_SUPPORTED) |
|
||||
if(OPUS_X86_MAY_HAVE_SSE) |
|
||||
add_sources_group(opus celt ${celt_sources_sse}) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_MAY_HAVE_SSE) |
|
||||
if(NOT MSVC) |
|
||||
set_source_files_properties(${celt_sources_sse} PROPERTIES COMPILE_FLAGS -msse) |
|
||||
endif() |
|
||||
endif() |
|
||||
if(OPUS_X86_PRESUME_SSE) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_PRESUME_SSE) |
|
||||
if(NOT MSVC) |
|
||||
target_compile_options(opus PRIVATE -msse) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(SSE2_SUPPORTED) |
|
||||
if(OPUS_X86_MAY_HAVE_SSE2) |
|
||||
add_sources_group(opus celt ${celt_sources_sse2}) |
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${dnn_sources_sse2}) |
|
||||
endif() |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_MAY_HAVE_SSE2) |
|
||||
if(NOT MSVC) |
|
||||
set_source_files_properties(${celt_sources_sse2} ${dnn_sources_sse2} PROPERTIES COMPILE_FLAGS -msse2) |
|
||||
endif() |
|
||||
endif() |
|
||||
if(OPUS_X86_PRESUME_SSE2) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_PRESUME_SSE2) |
|
||||
if(NOT MSVC) |
|
||||
target_compile_options(opus PRIVATE -msse2) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(SSE4_1_SUPPORTED) |
|
||||
if(OPUS_X86_MAY_HAVE_SSE4_1) |
|
||||
add_sources_group(opus celt ${celt_sources_sse4_1}) |
|
||||
add_sources_group(opus silk ${silk_sources_sse4_1}) |
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${dnn_sources_sse4_1}) |
|
||||
endif() |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_MAY_HAVE_SSE4_1) |
|
||||
if(NOT MSVC) |
|
||||
set_source_files_properties(${celt_sources_sse4_1} ${silk_sources_sse4_1} ${dnn_sources_sse4_1} PROPERTIES COMPILE_FLAGS -msse4.1) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_FIXED_POINT) |
|
||||
add_sources_group(opus silk ${silk_sources_fixed_sse4_1}) |
|
||||
if(NOT MSVC) |
|
||||
set_source_files_properties(${silk_sources_fixed_sse4_1} PROPERTIES COMPILE_FLAGS -msse4.1) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
if(OPUS_X86_PRESUME_SSE4_1) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_PRESUME_SSE4_1) |
|
||||
if(NOT MSVC) |
|
||||
target_compile_options(opus PRIVATE -msse4.1) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(AVX2_SUPPORTED) |
|
||||
if(OPUS_X86_MAY_HAVE_AVX2) |
|
||||
add_sources_group(opus celt ${celt_sources_avx2}) |
|
||||
add_sources_group(opus silk ${silk_sources_avx2}) |
|
||||
add_sources_group(opus silk ${silk_sources_float_avx2}) |
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${dnn_sources_avx2}) |
|
||||
endif() |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_MAY_HAVE_AVX2) |
|
||||
if(MSVC) |
|
||||
set(AVX2_FLAGS "${AVX2_FLAGS} /arch:AVX2") |
|
||||
else() |
|
||||
set(AVX2_FLAGS "${AVX2_FLAGS} -mavx2 -mfma -mavx") |
|
||||
endif() |
|
||||
set_source_files_properties(${celt_sources_avx2} PROPERTIES COMPILE_FLAGS ${AVX2_FLAGS}) |
|
||||
set_source_files_properties(${silk_sources_avx2} PROPERTIES COMPILE_FLAGS ${AVX2_FLAGS}) |
|
||||
set_source_files_properties(${silk_sources_float_avx2} PROPERTIES COMPILE_FLAGS ${AVX2_FLAGS}) |
|
||||
set_source_files_properties(${dnn_sources_avx2} PROPERTIES COMPILE_FLAGS ${AVX2_FLAGS}) |
|
||||
endif() |
|
||||
if(OPUS_X86_PRESUME_AVX2) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_PRESUME_AVX2) |
|
||||
target_compile_definitions(opus PRIVATE OPUS_X86_PRESUME_SSE4_1) |
|
||||
if(NOT MSVC) |
|
||||
target_compile_options(opus PRIVATE -mavx2 -mfma -mavx) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(MSVC) |
|
||||
if(AVX2_SUPPORTED AND OPUS_X86_PRESUME_AVX2) # on 64 bit and 32 bits |
|
||||
add_definitions(/arch:AVX2) |
|
||||
elseif(OPUS_CPU_X86) # if AVX not supported then set SSE flag |
|
||||
if((SSE4_1_SUPPORTED AND OPUS_X86_PRESUME_SSE4_1) |
|
||||
OR (SSE2_SUPPORTED AND OPUS_X86_PRESUME_SSE2)) |
|
||||
target_compile_definitions(opus PRIVATE /arch:SSE2) |
|
||||
elseif(SSE1_SUPPORTED AND OPUS_X86_PRESUME_SSE) |
|
||||
target_compile_definitions(opus PRIVATE /arch:SSE) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
if(COMPILER_SUPPORT_NEON) |
|
||||
if(OPUS_MAY_HAVE_NEON) |
|
||||
if(RUNTIME_CPU_CAPABILITY_DETECTION) |
|
||||
message(STATUS "OPUS_MAY_HAVE_NEON enabling runtime detection") |
|
||||
target_compile_definitions(opus PRIVATE OPUS_HAVE_RTCD) |
|
||||
add_sources_group(opus celt ${celt_sources_arm_rtcd}) |
|
||||
add_sources_group(opus silk ${silk_sources_arm_rtcd}) |
|
||||
else() |
|
||||
message(ERROR "Runtime cpu capability detection needed for MAY_HAVE_NEON") |
|
||||
endif() |
|
||||
# Do runtime check for NEON |
|
||||
target_compile_definitions(opus |
|
||||
PRIVATE |
|
||||
OPUS_ARM_MAY_HAVE_NEON |
|
||||
OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
endif() |
|
||||
|
|
||||
add_sources_group(opus celt ${celt_sources_arm_neon_intr}) |
|
||||
add_sources_group(opus silk ${silk_sources_arm_neon_intr}) |
|
||||
if (OPUS_DNN) |
|
||||
add_sources_group(opus lpcnet ${dnn_sources_arm_neon}) |
|
||||
endif() |
|
||||
|
|
||||
# silk arm neon depends on main_Fix.h |
|
||||
target_include_directories(opus PRIVATE silk/fixed) |
|
||||
|
|
||||
if(OPUS_FIXED_POINT) |
|
||||
add_sources_group(opus silk ${silk_sources_fixed_arm_neon_intr}) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_PRESUME_NEON) |
|
||||
target_compile_definitions(opus |
|
||||
PRIVATE |
|
||||
OPUS_ARM_PRESUME_NEON |
|
||||
OPUS_ARM_PRESUME_NEON_INTR) |
|
||||
endif() |
|
||||
endif() |
|
||||
endif() |
|
||||
|
|
||||
target_compile_definitions(opus |
|
||||
PRIVATE |
|
||||
$<$<BOOL:${HAVE_LRINT}>:HAVE_LRINT> |
|
||||
$<$<BOOL:${HAVE_LRINTF}>:HAVE_LRINTF>) |
|
||||
|
|
||||
if(OPUS_BUILD_FRAMEWORK) |
|
||||
set_target_properties(opus PROPERTIES |
|
||||
FRAMEWORK TRUE |
|
||||
FRAMEWORK_VERSION ${PROJECT_VERSION} |
|
||||
MACOSX_FRAMEWORK_IDENTIFIER org.xiph.opus |
|
||||
MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${PROJECT_VERSION} |
|
||||
MACOSX_FRAMEWORK_BUNDLE_VERSION ${PROJECT_VERSION} |
|
||||
XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" |
|
||||
OUTPUT_NAME Opus) |
|
||||
endif() |
|
||||
|
|
||||
install(TARGETS opus |
|
||||
EXPORT OpusTargets |
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} |
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} |
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} |
|
||||
FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX} |
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus) |
|
||||
|
|
||||
if(OPUS_INSTALL_PKG_CONFIG_MODULE) |
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX}) |
|
||||
set(exec_prefix ${CMAKE_INSTALL_PREFIX}) |
|
||||
set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) |
|
||||
set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) |
|
||||
set(VERSION ${PACKAGE_VERSION}) |
|
||||
if(HAVE_LIBM) |
|
||||
set(LIBM "-lm") |
|
||||
endif() |
|
||||
configure_file(opus.pc.in opus.pc) |
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc |
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_INSTALL_CMAKE_CONFIG_MODULE) |
|
||||
set(CPACK_GENERATOR TGZ) |
|
||||
include(CPack) |
|
||||
set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) |
|
||||
install(EXPORT OpusTargets |
|
||||
NAMESPACE Opus:: |
|
||||
DESTINATION ${CMAKE_INSTALL_PACKAGEDIR}) |
|
||||
|
|
||||
include(CMakePackageConfigHelpers) |
|
||||
|
|
||||
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}) |
|
||||
configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in |
|
||||
OpusConfig.cmake |
|
||||
INSTALL_DESTINATION |
|
||||
${CMAKE_INSTALL_PACKAGEDIR} |
|
||||
PATH_VARS |
|
||||
INCLUDE_INSTALL_DIR |
|
||||
INSTALL_PREFIX |
|
||||
${CMAKE_INSTALL_PREFIX}) |
|
||||
write_basic_package_version_file(OpusConfigVersion.cmake |
|
||||
VERSION ${PROJECT_VERSION} |
|
||||
COMPATIBILITY SameMajorVersion) |
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake |
|
||||
${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake |
|
||||
DESTINATION ${CMAKE_INSTALL_PACKAGEDIR}) |
|
||||
endif() |
|
||||
|
|
||||
if(OPUS_BUILD_PROGRAMS) |
|
||||
# demo |
|
||||
if(OPUS_CUSTOM_MODES) |
|
||||
add_executable(opus_custom_demo ${opus_custom_demo_sources}) |
|
||||
target_include_directories(opus_custom_demo |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_link_libraries(opus_custom_demo PRIVATE opus) |
|
||||
target_compile_definitions(opus_custom_demo PRIVATE OPUS_BUILD) |
|
||||
endif() |
|
||||
|
|
||||
add_executable(opus_demo ${opus_demo_sources}) |
|
||||
target_include_directories(opus_demo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_include_directories(opus_demo PRIVATE silk) # debug.h |
|
||||
target_include_directories(opus_demo PRIVATE celt) # arch.h |
|
||||
target_include_directories(opus_demo PRIVATE dnn) |
|
||||
target_link_libraries(opus_demo PRIVATE opus ${OPUS_REQUIRED_LIBRARIES}) |
|
||||
target_compile_definitions(opus_demo PRIVATE OPUS_BUILD) |
|
||||
|
|
||||
# compare |
|
||||
add_executable(opus_compare ${opus_compare_sources}) |
|
||||
target_include_directories(opus_compare PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_link_libraries(opus_compare PRIVATE opus ${OPUS_REQUIRED_LIBRARIES}) |
|
||||
endif() |
|
||||
|
|
||||
if(BUILD_TESTING AND NOT BUILD_SHARED_LIBS) |
|
||||
enable_testing() |
|
||||
|
|
||||
# tests |
|
||||
add_executable(test_opus_decode ${test_opus_decode_sources}) |
|
||||
target_include_directories(test_opus_decode |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_link_libraries(test_opus_decode PRIVATE opus) |
|
||||
target_compile_definitions(test_opus_decode PRIVATE OPUS_BUILD) |
|
||||
if(OPUS_FIXED_POINT) |
|
||||
target_compile_definitions(test_opus_decode PRIVATE DISABLE_FLOAT_API) |
|
||||
endif() |
|
||||
add_test(NAME test_opus_decode COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_decode> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
|
|
||||
add_executable(test_opus_padding ${test_opus_padding_sources}) |
|
||||
target_include_directories(test_opus_padding |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_link_libraries(test_opus_padding PRIVATE opus) |
|
||||
add_test(NAME test_opus_padding COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_padding> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
|
|
||||
add_executable(test_opus_api ${test_opus_api_sources}) |
|
||||
target_include_directories(test_opus_api |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} celt) |
|
||||
target_link_libraries(test_opus_api PRIVATE opus) |
|
||||
target_compile_definitions(test_opus_api PRIVATE OPUS_BUILD) |
|
||||
if(OPUS_FIXED_POINT) |
|
||||
target_compile_definitions(test_opus_api PRIVATE DISABLE_FLOAT_API) |
|
||||
endif() |
|
||||
add_test(NAME test_opus_api COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_api> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
|
|
||||
add_executable(test_opus_encode ${test_opus_encode_sources}) |
|
||||
target_include_directories(test_opus_encode |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} celt dnn) |
|
||||
target_link_libraries(test_opus_encode PRIVATE opus) |
|
||||
target_compile_definitions(test_opus_encode PRIVATE OPUS_BUILD) |
|
||||
add_test(NAME test_opus_encode COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_encode> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
|
|
||||
add_executable(test_opus_extensions ${test_opus_extensions_sources}) |
|
||||
target_include_directories(test_opus_extensions |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} celt dnn) |
|
||||
target_link_libraries(test_opus_extensions PRIVATE opus) |
|
||||
target_compile_definitions(test_opus_extensions PRIVATE OPUS_BUILD) |
|
||||
add_test(NAME test_opus_extensions COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_extensions> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
if(OPUS_DRED) |
|
||||
add_executable(test_opus_dred ${test_opus_dred_sources}) |
|
||||
target_include_directories(test_opus_dred |
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) |
|
||||
target_link_libraries(test_opus_dred PRIVATE opus) |
|
||||
target_compile_definitions(test_opus_dred PRIVATE OPUS_BUILD) |
|
||||
add_test(NAME test_opus_dred COMMAND ${CMAKE_COMMAND} |
|
||||
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_dred> |
|
||||
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} |
|
||||
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake") |
|
||||
endif() |
|
||||
endif() |
|
||||
@ -1,44 +0,0 @@ |
|||||
Copyright 2001-2023 Xiph.Org, Skype Limited, Octasic, |
|
||||
Jean-Marc Valin, Timothy B. Terriberry, |
|
||||
CSIRO, Gregory Maxwell, Mark Borgerding, |
|
||||
Erik de Castro Lopo, Mozilla, Amazon |
|
||||
|
|
||||
Redistribution and use in source and binary forms, with or without |
|
||||
modification, are permitted provided that the following conditions |
|
||||
are met: |
|
||||
|
|
||||
- Redistributions of source code must retain the above copyright |
|
||||
notice, this list of conditions and the following disclaimer. |
|
||||
|
|
||||
- Redistributions in binary form must reproduce the above copyright |
|
||||
notice, this list of conditions and the following disclaimer in the |
|
||||
documentation and/or other materials provided with the distribution. |
|
||||
|
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the |
|
||||
names of specific contributors, may be used to endorse or promote |
|
||||
products derived from this software without specific prior written |
|
||||
permission. |
|
||||
|
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
|
|
||||
Opus is subject to the royalty-free patent licenses which are |
|
||||
specified at: |
|
||||
|
|
||||
Xiph.Org Foundation: |
|
||||
https://datatracker.ietf.org/ipr/1524/ |
|
||||
|
|
||||
Microsoft Corporation: |
|
||||
https://datatracker.ietf.org/ipr/1914/ |
|
||||
|
|
||||
Broadcom Corporation: |
|
||||
https://datatracker.ietf.org/ipr/1526/ |
|
||||
@ -1,368 +0,0 @@ |
|||||
Installation Instructions |
|
||||
************************* |
|
||||
|
|
||||
Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free |
|
||||
Software Foundation, Inc. |
|
||||
|
|
||||
Copying and distribution of this file, with or without modification, |
|
||||
are permitted in any medium without royalty provided the copyright |
|
||||
notice and this notice are preserved. This file is offered as-is, |
|
||||
without warranty of any kind. |
|
||||
|
|
||||
Basic Installation |
|
||||
================== |
|
||||
|
|
||||
Briefly, the shell command './configure && make && make install' |
|
||||
should configure, build, and install this package. The following |
|
||||
more-detailed instructions are generic; see the 'README' file for |
|
||||
instructions specific to this package. Some packages provide this |
|
||||
'INSTALL' file but do not implement all of the features documented |
|
||||
below. The lack of an optional feature in a given package is not |
|
||||
necessarily a bug. More recommendations for GNU packages can be found |
|
||||
in *note Makefile Conventions: (standards)Makefile Conventions. |
|
||||
|
|
||||
The 'configure' shell script attempts to guess correct values for |
|
||||
various system-dependent variables used during compilation. It uses |
|
||||
those values to create a 'Makefile' in each directory of the package. |
|
||||
It may also create one or more '.h' files containing system-dependent |
|
||||
definitions. Finally, it creates a shell script 'config.status' that |
|
||||
you can run in the future to recreate the current configuration, and a |
|
||||
file 'config.log' containing compiler output (useful mainly for |
|
||||
debugging 'configure'). |
|
||||
|
|
||||
It can also use an optional file (typically called 'config.cache' and |
|
||||
enabled with '--cache-file=config.cache' or simply '-C') that saves the |
|
||||
results of its tests to speed up reconfiguring. Caching is disabled by |
|
||||
default to prevent problems with accidental use of stale cache files. |
|
||||
|
|
||||
If you need to do unusual things to compile the package, please try |
|
||||
to figure out how 'configure' could check whether to do them, and mail |
|
||||
diffs or instructions to the address given in the 'README' so they can |
|
||||
be considered for the next release. If you are using the cache, and at |
|
||||
some point 'config.cache' contains results you don't want to keep, you |
|
||||
may remove or edit it. |
|
||||
|
|
||||
The file 'configure.ac' (or 'configure.in') is used to create |
|
||||
'configure' by a program called 'autoconf'. You need 'configure.ac' if |
|
||||
you want to change it or regenerate 'configure' using a newer version of |
|
||||
'autoconf'. |
|
||||
|
|
||||
The simplest way to compile this package is: |
|
||||
|
|
||||
1. 'cd' to the directory containing the package's source code and type |
|
||||
'./configure' to configure the package for your system. |
|
||||
|
|
||||
Running 'configure' might take a while. While running, it prints |
|
||||
some messages telling which features it is checking for. |
|
||||
|
|
||||
2. Type 'make' to compile the package. |
|
||||
|
|
||||
3. Optionally, type 'make check' to run any self-tests that come with |
|
||||
the package, generally using the just-built uninstalled binaries. |
|
||||
|
|
||||
4. Type 'make install' to install the programs and any data files and |
|
||||
documentation. When installing into a prefix owned by root, it is |
|
||||
recommended that the package be configured and built as a regular |
|
||||
user, and only the 'make install' phase executed with root |
|
||||
privileges. |
|
||||
|
|
||||
5. Optionally, type 'make installcheck' to repeat any self-tests, but |
|
||||
this time using the binaries in their final installed location. |
|
||||
This target does not install anything. Running this target as a |
|
||||
regular user, particularly if the prior 'make install' required |
|
||||
root privileges, verifies that the installation completed |
|
||||
correctly. |
|
||||
|
|
||||
6. You can remove the program binaries and object files from the |
|
||||
source code directory by typing 'make clean'. To also remove the |
|
||||
files that 'configure' created (so you can compile the package for |
|
||||
a different kind of computer), type 'make distclean'. There is |
|
||||
also a 'make maintainer-clean' target, but that is intended mainly |
|
||||
for the package's developers. If you use it, you may have to get |
|
||||
all sorts of other programs in order to regenerate files that came |
|
||||
with the distribution. |
|
||||
|
|
||||
7. Often, you can also type 'make uninstall' to remove the installed |
|
||||
files again. In practice, not all packages have tested that |
|
||||
uninstallation works correctly, even though it is required by the |
|
||||
GNU Coding Standards. |
|
||||
|
|
||||
8. Some packages, particularly those that use Automake, provide 'make |
|
||||
distcheck', which can by used by developers to test that all other |
|
||||
targets like 'make install' and 'make uninstall' work correctly. |
|
||||
This target is generally not run by end users. |
|
||||
|
|
||||
Compilers and Options |
|
||||
===================== |
|
||||
|
|
||||
Some systems require unusual options for compilation or linking that |
|
||||
the 'configure' script does not know about. Run './configure --help' |
|
||||
for details on some of the pertinent environment variables. |
|
||||
|
|
||||
You can give 'configure' initial values for configuration parameters |
|
||||
by setting variables in the command line or in the environment. Here is |
|
||||
an example: |
|
||||
|
|
||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix |
|
||||
|
|
||||
*Note Defining Variables::, for more details. |
|
||||
|
|
||||
Compiling For Multiple Architectures |
|
||||
==================================== |
|
||||
|
|
||||
You can compile the package for more than one kind of computer at the |
|
||||
same time, by placing the object files for each architecture in their |
|
||||
own directory. To do this, you can use GNU 'make'. 'cd' to the |
|
||||
directory where you want the object files and executables to go and run |
|
||||
the 'configure' script. 'configure' automatically checks for the source |
|
||||
code in the directory that 'configure' is in and in '..'. This is known |
|
||||
as a "VPATH" build. |
|
||||
|
|
||||
With a non-GNU 'make', it is safer to compile the package for one |
|
||||
architecture at a time in the source code directory. After you have |
|
||||
installed the package for one architecture, use 'make distclean' before |
|
||||
reconfiguring for another architecture. |
|
||||
|
|
||||
On MacOS X 10.5 and later systems, you can create libraries and |
|
||||
executables that work on multiple system types--known as "fat" or |
|
||||
"universal" binaries--by specifying multiple '-arch' options to the |
|
||||
compiler but only a single '-arch' option to the preprocessor. Like |
|
||||
this: |
|
||||
|
|
||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ |
|
||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ |
|
||||
CPP="gcc -E" CXXCPP="g++ -E" |
|
||||
|
|
||||
This is not guaranteed to produce working output in all cases, you |
|
||||
may have to build one architecture at a time and combine the results |
|
||||
using the 'lipo' tool if you have problems. |
|
||||
|
|
||||
Installation Names |
|
||||
================== |
|
||||
|
|
||||
By default, 'make install' installs the package's commands under |
|
||||
'/usr/local/bin', include files under '/usr/local/include', etc. You |
|
||||
can specify an installation prefix other than '/usr/local' by giving |
|
||||
'configure' the option '--prefix=PREFIX', where PREFIX must be an |
|
||||
absolute file name. |
|
||||
|
|
||||
You can specify separate installation prefixes for |
|
||||
architecture-specific files and architecture-independent files. If you |
|
||||
pass the option '--exec-prefix=PREFIX' to 'configure', the package uses |
|
||||
PREFIX as the prefix for installing programs and libraries. |
|
||||
Documentation and other data files still use the regular prefix. |
|
||||
|
|
||||
In addition, if you use an unusual directory layout you can give |
|
||||
options like '--bindir=DIR' to specify different values for particular |
|
||||
kinds of files. Run 'configure --help' for a list of the directories |
|
||||
you can set and what kinds of files go in them. In general, the default |
|
||||
for these options is expressed in terms of '${prefix}', so that |
|
||||
specifying just '--prefix' will affect all of the other directory |
|
||||
specifications that were not explicitly provided. |
|
||||
|
|
||||
The most portable way to affect installation locations is to pass the |
|
||||
correct locations to 'configure'; however, many packages provide one or |
|
||||
both of the following shortcuts of passing variable assignments to the |
|
||||
'make install' command line to change installation locations without |
|
||||
having to reconfigure or recompile. |
|
||||
|
|
||||
The first method involves providing an override variable for each |
|
||||
affected directory. For example, 'make install |
|
||||
prefix=/alternate/directory' will choose an alternate location for all |
|
||||
directory configuration variables that were expressed in terms of |
|
||||
'${prefix}'. Any directories that were specified during 'configure', |
|
||||
but not in terms of '${prefix}', must each be overridden at install time |
|
||||
for the entire installation to be relocated. The approach of makefile |
|
||||
variable overrides for each directory variable is required by the GNU |
|
||||
Coding Standards, and ideally causes no recompilation. However, some |
|
||||
platforms have known limitations with the semantics of shared libraries |
|
||||
that end up requiring recompilation when using this method, particularly |
|
||||
noticeable in packages that use GNU Libtool. |
|
||||
|
|
||||
The second method involves providing the 'DESTDIR' variable. For |
|
||||
example, 'make install DESTDIR=/alternate/directory' will prepend |
|
||||
'/alternate/directory' before all installation names. The approach of |
|
||||
'DESTDIR' overrides is not required by the GNU Coding Standards, and |
|
||||
does not work on platforms that have drive letters. On the other hand, |
|
||||
it does better at avoiding recompilation issues, and works well even |
|
||||
when some directory options were not specified in terms of '${prefix}' |
|
||||
at 'configure' time. |
|
||||
|
|
||||
Optional Features |
|
||||
================= |
|
||||
|
|
||||
If the package supports it, you can cause programs to be installed |
|
||||
with an extra prefix or suffix on their names by giving 'configure' the |
|
||||
option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. |
|
||||
|
|
||||
Some packages pay attention to '--enable-FEATURE' options to |
|
||||
'configure', where FEATURE indicates an optional part of the package. |
|
||||
They may also pay attention to '--with-PACKAGE' options, where PACKAGE |
|
||||
is something like 'gnu-as' or 'x' (for the X Window System). The |
|
||||
'README' should mention any '--enable-' and '--with-' options that the |
|
||||
package recognizes. |
|
||||
|
|
||||
For packages that use the X Window System, 'configure' can usually |
|
||||
find the X include and library files automatically, but if it doesn't, |
|
||||
you can use the 'configure' options '--x-includes=DIR' and |
|
||||
'--x-libraries=DIR' to specify their locations. |
|
||||
|
|
||||
Some packages offer the ability to configure how verbose the |
|
||||
execution of 'make' will be. For these packages, running './configure |
|
||||
--enable-silent-rules' sets the default to minimal output, which can be |
|
||||
overridden with 'make V=1'; while running './configure |
|
||||
--disable-silent-rules' sets the default to verbose, which can be |
|
||||
overridden with 'make V=0'. |
|
||||
|
|
||||
Particular systems |
|
||||
================== |
|
||||
|
|
||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC |
|
||||
is not installed, it is recommended to use the following options in |
|
||||
order to use an ANSI C compiler: |
|
||||
|
|
||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500" |
|
||||
|
|
||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX. |
|
||||
|
|
||||
HP-UX 'make' updates targets which have the same timestamps as their |
|
||||
prerequisites, which makes it generally unusable when shipped generated |
|
||||
files such as 'configure' are involved. Use GNU 'make' instead. |
|
||||
|
|
||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot |
|
||||
parse its '<wchar.h>' header file. The option '-nodtk' can be used as a |
|
||||
workaround. If GNU CC is not installed, it is therefore recommended to |
|
||||
try |
|
||||
|
|
||||
./configure CC="cc" |
|
||||
|
|
||||
and if that doesn't work, try |
|
||||
|
|
||||
./configure CC="cc -nodtk" |
|
||||
|
|
||||
On Solaris, don't put '/usr/ucb' early in your 'PATH'. This |
|
||||
directory contains several dysfunctional programs; working variants of |
|
||||
these programs are available in '/usr/bin'. So, if you need '/usr/ucb' |
|
||||
in your 'PATH', put it _after_ '/usr/bin'. |
|
||||
|
|
||||
On Haiku, software installed for all users goes in '/boot/common', |
|
||||
not '/usr/local'. It is recommended to use the following options: |
|
||||
|
|
||||
./configure --prefix=/boot/common |
|
||||
|
|
||||
Specifying the System Type |
|
||||
========================== |
|
||||
|
|
||||
There may be some features 'configure' cannot figure out |
|
||||
automatically, but needs to determine by the type of machine the package |
|
||||
will run on. Usually, assuming the package is built to be run on the |
|
||||
_same_ architectures, 'configure' can figure that out, but if it prints |
|
||||
a message saying it cannot guess the machine type, give it the |
|
||||
'--build=TYPE' option. TYPE can either be a short name for the system |
|
||||
type, such as 'sun4', or a canonical name which has the form: |
|
||||
|
|
||||
CPU-COMPANY-SYSTEM |
|
||||
|
|
||||
where SYSTEM can have one of these forms: |
|
||||
|
|
||||
OS |
|
||||
KERNEL-OS |
|
||||
|
|
||||
See the file 'config.sub' for the possible values of each field. If |
|
||||
'config.sub' isn't included in this package, then this package doesn't |
|
||||
need to know the machine type. |
|
||||
|
|
||||
If you are _building_ compiler tools for cross-compiling, you should |
|
||||
use the option '--target=TYPE' to select the type of system they will |
|
||||
produce code for. |
|
||||
|
|
||||
If you want to _use_ a cross compiler, that generates code for a |
|
||||
platform different from the build platform, you should specify the |
|
||||
"host" platform (i.e., that on which the generated programs will |
|
||||
eventually be run) with '--host=TYPE'. |
|
||||
|
|
||||
Sharing Defaults |
|
||||
================ |
|
||||
|
|
||||
If you want to set default values for 'configure' scripts to share, |
|
||||
you can create a site shell script called 'config.site' that gives |
|
||||
default values for variables like 'CC', 'cache_file', and 'prefix'. |
|
||||
'configure' looks for 'PREFIX/share/config.site' if it exists, then |
|
||||
'PREFIX/etc/config.site' if it exists. Or, you can set the |
|
||||
'CONFIG_SITE' environment variable to the location of the site script. |
|
||||
A warning: not all 'configure' scripts look for a site script. |
|
||||
|
|
||||
Defining Variables |
|
||||
================== |
|
||||
|
|
||||
Variables not defined in a site shell script can be set in the |
|
||||
environment passed to 'configure'. However, some packages may run |
|
||||
configure again during the build, and the customized values of these |
|
||||
variables may be lost. In order to avoid this problem, you should set |
|
||||
them in the 'configure' command line, using 'VAR=value'. For example: |
|
||||
|
|
||||
./configure CC=/usr/local2/bin/gcc |
|
||||
|
|
||||
causes the specified 'gcc' to be used as the C compiler (unless it is |
|
||||
overridden in the site shell script). |
|
||||
|
|
||||
Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an |
|
||||
Autoconf limitation. Until the limitation is lifted, you can use this |
|
||||
workaround: |
|
||||
|
|
||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash |
|
||||
|
|
||||
'configure' Invocation |
|
||||
====================== |
|
||||
|
|
||||
'configure' recognizes the following options to control how it |
|
||||
operates. |
|
||||
|
|
||||
'--help' |
|
||||
'-h' |
|
||||
Print a summary of all of the options to 'configure', and exit. |
|
||||
|
|
||||
'--help=short' |
|
||||
'--help=recursive' |
|
||||
Print a summary of the options unique to this package's |
|
||||
'configure', and exit. The 'short' variant lists options used only |
|
||||
in the top level, while the 'recursive' variant lists options also |
|
||||
present in any nested packages. |
|
||||
|
|
||||
'--version' |
|
||||
'-V' |
|
||||
Print the version of Autoconf used to generate the 'configure' |
|
||||
script, and exit. |
|
||||
|
|
||||
'--cache-file=FILE' |
|
||||
Enable the cache: use and save the results of the tests in FILE, |
|
||||
traditionally 'config.cache'. FILE defaults to '/dev/null' to |
|
||||
disable caching. |
|
||||
|
|
||||
'--config-cache' |
|
||||
'-C' |
|
||||
Alias for '--cache-file=config.cache'. |
|
||||
|
|
||||
'--quiet' |
|
||||
'--silent' |
|
||||
'-q' |
|
||||
Do not print messages saying which checks are being made. To |
|
||||
suppress all normal output, redirect it to '/dev/null' (any error |
|
||||
messages will still be shown). |
|
||||
|
|
||||
'--srcdir=DIR' |
|
||||
Look for the package's source code in directory DIR. Usually |
|
||||
'configure' can determine that directory automatically. |
|
||||
|
|
||||
'--prefix=DIR' |
|
||||
Use DIR as the installation prefix. *note Installation Names:: for |
|
||||
more details, including other options available for fine-tuning the |
|
||||
installation locations. |
|
||||
|
|
||||
'--no-create' |
|
||||
'-n' |
|
||||
Run the configure checks, but stop before creating any output |
|
||||
files. |
|
||||
|
|
||||
'configure' also accepts some other, not widely useful, options. Run |
|
||||
'configure --help' for more details. |
|
||||
@ -1,489 +0,0 @@ |
|||||
# Provide the full test output for failed tests when using the parallel
|
|
||||
# test suite (which is enabled by default with automake 1.13+).
|
|
||||
export VERBOSE = yes |
|
||||
|
|
||||
AUTOMAKE_OPTIONS = subdir-objects |
|
||||
ACLOCAL_AMFLAGS = -I m4 |
|
||||
|
|
||||
lib_LTLIBRARIES = libopus.la |
|
||||
|
|
||||
DIST_SUBDIRS = doc |
|
||||
|
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/celt -I$(top_srcdir)/silk \
|
|
||||
-I$(top_srcdir)/silk/float -I$(top_srcdir)/silk/fixed $(NE10_CFLAGS) \
|
|
||||
-I$(top_srcdir)/dnn |
|
||||
|
|
||||
include celt_sources.mk |
|
||||
include lpcnet_sources.mk |
|
||||
include silk_sources.mk |
|
||||
include opus_sources.mk |
|
||||
|
|
||||
LPCNET_SOURCES = |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DEEP_PLC_SOURCES) |
|
||||
endif |
|
||||
if ENABLE_DRED |
|
||||
LPCNET_SOURCES += $(DRED_SOURCES) |
|
||||
endif |
|
||||
if ENABLE_OSCE |
|
||||
LPCNET_SOURCES += $(OSCE_SOURCES) |
|
||||
endif |
|
||||
|
|
||||
if FIXED_POINT |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FIXED) |
|
||||
if HAVE_SSE4_1 |
|
||||
SILK_SOURCES += $(SILK_SOURCES_SSE4_1) $(SILK_SOURCES_FIXED_SSE4_1) |
|
||||
endif |
|
||||
if HAVE_ARM_NEON_INTR |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FIXED_ARM_NEON_INTR) |
|
||||
endif |
|
||||
else |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FLOAT) |
|
||||
if HAVE_SSE4_1 |
|
||||
SILK_SOURCES += $(SILK_SOURCES_SSE4_1) |
|
||||
endif |
|
||||
if HAVE_AVX2 |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FLOAT_AVX2) |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
if DISABLE_FLOAT_API |
|
||||
else |
|
||||
OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) |
|
||||
endif |
|
||||
|
|
||||
if CPU_X86 |
|
||||
if HAVE_RTCD |
|
||||
CELT_SOURCES += $(CELT_SOURCES_X86_RTCD) |
|
||||
SILK_SOURCES += $(SILK_SOURCES_X86_RTCD) |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_X86_RTCD) |
|
||||
endif |
|
||||
endif |
|
||||
if HAVE_SSE |
|
||||
CELT_SOURCES += $(CELT_SOURCES_SSE) |
|
||||
endif |
|
||||
if HAVE_SSE2 |
|
||||
CELT_SOURCES += $(CELT_SOURCES_SSE2) |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_SSE2) |
|
||||
endif |
|
||||
endif |
|
||||
if HAVE_SSE4_1 |
|
||||
CELT_SOURCES += $(CELT_SOURCES_SSE4_1) |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_SSE4_1) |
|
||||
endif |
|
||||
endif |
|
||||
if HAVE_AVX2 |
|
||||
SILK_SOURCES += $(SILK_SOURCES_AVX2) |
|
||||
CELT_SOURCES += $(CELT_SOURCES_AVX2) |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_AVX2) |
|
||||
endif |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
if CPU_ARM |
|
||||
if HAVE_RTCD |
|
||||
CELT_SOURCES += $(CELT_SOURCES_ARM_RTCD) |
|
||||
SILK_SOURCES += $(SILK_SOURCES_ARM_RTCD) |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_ARM_RTCD) |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
if ENABLE_DEEP_PLC |
|
||||
if HAVE_ARM_DOTPROD |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_DOTPROD) |
|
||||
endif |
|
||||
if HAVE_ARM_NEON_INTR |
|
||||
LPCNET_SOURCES += $(DNN_SOURCES_NEON) |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
if HAVE_ARM_NEON_INTR |
|
||||
CELT_SOURCES += $(CELT_SOURCES_ARM_NEON_INTR) |
|
||||
SILK_SOURCES += $(SILK_SOURCES_ARM_NEON_INTR) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_ARM_NE10 |
|
||||
CELT_SOURCES += $(CELT_SOURCES_ARM_NE10) |
|
||||
endif |
|
||||
|
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
noinst_LTLIBRARIES = libarmasm.la |
|
||||
libarmasm_la_SOURCES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) |
|
||||
BUILT_SOURCES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) \
|
|
||||
$(CELT_AM_SOURCES_ARM_ASM:.s.in=.s) \
|
|
||||
$(CELT_AM_SOURCES_ARM_ASM:.s.in=-gnu.S) |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
CLEANFILES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) \
|
|
||||
$(CELT_AM_SOURCES_ARM_ASM:.s.in=-gnu.S) |
|
||||
|
|
||||
include celt_headers.mk |
|
||||
include lpcnet_headers.mk |
|
||||
include silk_headers.mk |
|
||||
include opus_headers.mk |
|
||||
|
|
||||
LPCNET_HEAD = |
|
||||
if ENABLE_DEEP_PLC |
|
||||
LPCNET_HEAD += $(DEEP_PLC_HEAD) |
|
||||
endif |
|
||||
if ENABLE_DRED |
|
||||
LPCNET_HEAD += $(DRED_HEAD) |
|
||||
endif |
|
||||
if ENABLE_OSCE |
|
||||
LPCNET_HEAD += $(OSCE_HEAD) |
|
||||
endif |
|
||||
if ENABLE_LOSSGEN |
|
||||
LPCNET_HEAD += $(LOSSGEN_HEAD) |
|
||||
endif |
|
||||
|
|
||||
libopus_la_SOURCES = $(CELT_SOURCES) $(SILK_SOURCES) $(LPCNET_SOURCES) $(OPUS_SOURCES) |
|
||||
libopus_la_LDFLAGS = -no-undefined -version-info @OPUS_LT_CURRENT@:@OPUS_LT_REVISION@:@OPUS_LT_AGE@ |
|
||||
libopus_la_LIBADD = $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
libopus_la_LIBADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
pkginclude_HEADERS = include/opus.h include/opus_multistream.h include/opus_types.h include/opus_defines.h include/opus_projection.h |
|
||||
|
|
||||
noinst_HEADERS = $(OPUS_HEAD) $(SILK_HEAD) $(CELT_HEAD) $(LPCNET_HEAD) |
|
||||
|
|
||||
if EXTRA_PROGRAMS |
|
||||
noinst_PROGRAMS = celt/tests/test_unit_cwrs32 \
|
|
||||
celt/tests/test_unit_dft \
|
|
||||
celt/tests/test_unit_entropy \
|
|
||||
celt/tests/test_unit_laplace \
|
|
||||
celt/tests/test_unit_mathops \
|
|
||||
celt/tests/test_unit_mdct \
|
|
||||
celt/tests/test_unit_rotation \
|
|
||||
celt/tests/test_unit_types \
|
|
||||
opus_compare \
|
|
||||
opus_demo \
|
|
||||
repacketizer_demo \
|
|
||||
silk/tests/test_unit_LPC_inv_pred_gain \
|
|
||||
tests/test_opus_api \
|
|
||||
tests/test_opus_decode \
|
|
||||
tests/test_opus_dred \
|
|
||||
tests/test_opus_encode \
|
|
||||
tests/test_opus_extensions \
|
|
||||
tests/test_opus_padding \
|
|
||||
tests/test_opus_projection \
|
|
||||
trivial_example |
|
||||
|
|
||||
TESTS = celt/tests/test_unit_cwrs32 \
|
|
||||
celt/tests/test_unit_dft \
|
|
||||
celt/tests/test_unit_entropy \
|
|
||||
celt/tests/test_unit_laplace \
|
|
||||
celt/tests/test_unit_mathops \
|
|
||||
celt/tests/test_unit_mdct \
|
|
||||
celt/tests/test_unit_rotation \
|
|
||||
celt/tests/test_unit_types \
|
|
||||
silk/tests/test_unit_LPC_inv_pred_gain \
|
|
||||
tests/test_opus_api \
|
|
||||
tests/test_opus_decode \
|
|
||||
tests/test_opus_encode \
|
|
||||
tests/test_opus_extensions \
|
|
||||
tests/test_opus_padding \
|
|
||||
tests/test_opus_projection |
|
||||
|
|
||||
opus_demo_SOURCES = src/opus_demo.c |
|
||||
if ENABLE_LOSSGEN |
|
||||
opus_demo_SOURCES += $(LOSSGEN_SOURCES) |
|
||||
endif |
|
||||
|
|
||||
opus_demo_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
repacketizer_demo_SOURCES = src/repacketizer_demo.c |
|
||||
|
|
||||
repacketizer_demo_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
opus_compare_SOURCES = src/opus_compare.c |
|
||||
opus_compare_LDADD = $(LIBM) |
|
||||
|
|
||||
trivial_example_SOURCES = doc/trivial_example.c |
|
||||
trivial_example_LDADD = libopus.la $(LIBM) |
|
||||
|
|
||||
tests_test_opus_api_SOURCES = tests/test_opus_api.c tests/test_opus_common.h |
|
||||
tests_test_opus_api_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
tests_test_opus_encode_SOURCES = tests/test_opus_encode.c tests/opus_encode_regressions.c tests/test_opus_common.h |
|
||||
tests_test_opus_encode_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
tests_test_opus_decode_SOURCES = tests/test_opus_decode.c tests/test_opus_common.h |
|
||||
tests_test_opus_decode_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
tests_test_opus_padding_SOURCES = tests/test_opus_padding.c tests/test_opus_common.h |
|
||||
tests_test_opus_padding_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
tests_test_opus_dred_SOURCES = tests/test_opus_dred.c tests/test_opus_common.h |
|
||||
tests_test_opus_dred_LDADD = libopus.la $(NE10_LIBS) $(LIBM) |
|
||||
|
|
||||
CELT_OBJ = $(CELT_SOURCES:.c=.lo) |
|
||||
SILK_OBJ = $(SILK_SOURCES:.c=.lo) |
|
||||
LPCNET_OBJ = $(LPCNET_SOURCES:.c=.lo) |
|
||||
OPUS_OBJ = $(OPUS_SOURCES:.c=.lo) |
|
||||
|
|
||||
tests_test_opus_extensions_SOURCES = tests/test_opus_extensions.c tests/test_opus_common.h |
|
||||
tests_test_opus_extensions_LDADD = $(OPUS_OBJ) $(SILK_OBJ) $(LPCNET_OBJ) $(CELT_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
tests_test_opus_extensions_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
tests_test_opus_projection_SOURCES = tests/test_opus_projection.c tests/test_opus_common.h |
|
||||
tests_test_opus_projection_LDADD = $(OPUS_OBJ) $(SILK_OBJ) $(LPCNET_OBJ) $(CELT_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
tests_test_opus_projection_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
silk_tests_test_unit_LPC_inv_pred_gain_SOURCES = silk/tests/test_unit_LPC_inv_pred_gain.c |
|
||||
silk_tests_test_unit_LPC_inv_pred_gain_LDADD = $(SILK_OBJ) $(LPCNET_OBJ) $(CELT_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
silk_tests_test_unit_LPC_inv_pred_gain_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
celt_tests_test_unit_cwrs32_SOURCES = celt/tests/test_unit_cwrs32.c |
|
||||
celt_tests_test_unit_cwrs32_LDADD = $(LIBM) |
|
||||
|
|
||||
celt_tests_test_unit_dft_SOURCES = celt/tests/test_unit_dft.c |
|
||||
celt_tests_test_unit_dft_LDADD = $(CELT_OBJ) $(LPCNET_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
celt_tests_test_unit_dft_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
celt_tests_test_unit_entropy_SOURCES = celt/tests/test_unit_entropy.c |
|
||||
celt_tests_test_unit_entropy_LDADD = $(LIBM) |
|
||||
|
|
||||
celt_tests_test_unit_laplace_SOURCES = celt/tests/test_unit_laplace.c |
|
||||
celt_tests_test_unit_laplace_LDADD = $(LIBM) |
|
||||
|
|
||||
celt_tests_test_unit_mathops_SOURCES = celt/tests/test_unit_mathops.c |
|
||||
celt_tests_test_unit_mathops_LDADD = $(CELT_OBJ) $(LPCNET_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
celt_tests_test_unit_mathops_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
celt_tests_test_unit_mdct_SOURCES = celt/tests/test_unit_mdct.c |
|
||||
celt_tests_test_unit_mdct_LDADD = $(CELT_OBJ) $(LPCNET_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
celt_tests_test_unit_mdct_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
celt_tests_test_unit_rotation_SOURCES = celt/tests/test_unit_rotation.c |
|
||||
celt_tests_test_unit_rotation_LDADD = $(CELT_OBJ) $(LPCNET_OBJ) $(NE10_LIBS) $(LIBM) |
|
||||
if OPUS_ARM_EXTERNAL_ASM |
|
||||
celt_tests_test_unit_rotation_LDADD += libarmasm.la |
|
||||
endif |
|
||||
|
|
||||
celt_tests_test_unit_types_SOURCES = celt/tests/test_unit_types.c |
|
||||
celt_tests_test_unit_types_LDADD = $(LIBM) |
|
||||
endif |
|
||||
|
|
||||
if CUSTOM_MODES |
|
||||
pkginclude_HEADERS += include/opus_custom.h |
|
||||
if EXTRA_PROGRAMS |
|
||||
noinst_PROGRAMS += opus_custom_demo |
|
||||
opus_custom_demo_SOURCES = celt/opus_custom_demo.c |
|
||||
opus_custom_demo_LDADD = libopus.la $(LIBM) |
|
||||
endif |
|
||||
endif |
|
||||
|
|
||||
if EXTRA_PROGRAMS |
|
||||
if ENABLE_DEEP_PLC |
|
||||
noinst_PROGRAMS += fargan_demo dump_data dump_weights_blob |
|
||||
fargan_demo_SOURCES = dnn/fargan_demo.c |
|
||||
fargan_demo_LDADD = $(LPCNET_OBJ) $(CELT_OBJ) $(LIBM) |
|
||||
|
|
||||
dump_data_SOURCES = dnn/dump_data.c |
|
||||
dump_data_LDADD = $(LPCNET_OBJ) $(CELT_OBJ) $(LIBM) |
|
||||
|
|
||||
dump_weights_blob_SOURCES = dnn/write_lpcnet_weights.c |
|
||||
dump_weights_blob_LDADD = $(LIBM) |
|
||||
dump_weights_blob_CFLAGS = $(AM_CFLAGS) -DDUMP_BINARY_WEIGHTS |
|
||||
endif |
|
||||
if ENABLE_DRED |
|
||||
TESTS += tests/test_opus_dred |
|
||||
endif |
|
||||
|
|
||||
if ENABLE_LOSSGEN |
|
||||
noinst_PROGRAMS += lossgen_demo |
|
||||
lossgen_demo_SOURCES = dnn/lossgen_demo.c $(LOSSGEN_SOURCES) |
|
||||
lossgen_demo_LDADD = $(LIBM) |
|
||||
endif |
|
||||
|
|
||||
endif |
|
||||
|
|
||||
|
|
||||
EXTRA_DIST = opus.pc.in \
|
|
||||
opus-uninstalled.pc.in \
|
|
||||
opus.m4 \
|
|
||||
Makefile.mips \
|
|
||||
Makefile.unix \
|
|
||||
CMakeLists.txt \
|
|
||||
cmake/CFeatureCheck.cmake \
|
|
||||
cmake/OpusBuildtype.cmake \
|
|
||||
cmake/OpusConfig.cmake \
|
|
||||
cmake/OpusConfig.cmake.in \
|
|
||||
cmake/OpusFunctions.cmake \
|
|
||||
cmake/OpusPackageVersion.cmake \
|
|
||||
cmake/OpusSources.cmake \
|
|
||||
cmake/README.md \
|
|
||||
cmake/RunTest.cmake \
|
|
||||
cmake/config.h.cmake.in \
|
|
||||
cmake/vla.c \
|
|
||||
cmake/cpu_info_by_asm.c \
|
|
||||
cmake/cpu_info_by_c.c \
|
|
||||
meson/get-version.py \
|
|
||||
meson/read-sources-list.py \
|
|
||||
meson/README.md \
|
|
||||
meson.build \
|
|
||||
meson_options.txt \
|
|
||||
include/meson.build \
|
|
||||
celt/meson.build \
|
|
||||
celt/tests/meson.build \
|
|
||||
dnn/meson.build \
|
|
||||
dnn/README.md \
|
|
||||
silk/meson.build \
|
|
||||
silk/tests/meson.build \
|
|
||||
src/meson.build \
|
|
||||
tests/meson.build \
|
|
||||
doc/meson.build \
|
|
||||
tests/run_vectors.sh \
|
|
||||
celt/arm/arm2gnu.pl \
|
|
||||
celt/arm/celt_pitch_xcorr_arm.s |
|
||||
|
|
||||
pkgconfigdir = $(libdir)/pkgconfig |
|
||||
pkgconfig_DATA = opus.pc |
|
||||
|
|
||||
m4datadir = $(datadir)/aclocal |
|
||||
m4data_DATA = opus.m4 |
|
||||
|
|
||||
# Targets to build and install just the library without the docs
|
|
||||
opus check-opus install-opus: export NO_DOXYGEN = 1 |
|
||||
|
|
||||
opus: all |
|
||||
check-opus: check |
|
||||
install-opus: install |
|
||||
|
|
||||
|
|
||||
# Or just the docs
|
|
||||
docs: |
|
||||
( cd doc && $(MAKE) $(AM_MAKEFLAGS) ) |
|
||||
|
|
||||
install-docs: |
|
||||
( cd doc && $(MAKE) $(AM_MAKEFLAGS) install ) |
|
||||
|
|
||||
|
|
||||
# Or everything (by default)
|
|
||||
all-local: |
|
||||
@[ -n "$(NO_DOXYGEN)" ] || ( cd doc && $(MAKE) $(AM_MAKEFLAGS) ) |
|
||||
|
|
||||
install-data-local: |
|
||||
@[ -n "$(NO_DOXYGEN)" ] || ( cd doc && $(MAKE) $(AM_MAKEFLAGS) install ) |
|
||||
|
|
||||
clean-local: |
|
||||
-( cd doc && $(MAKE) $(AM_MAKEFLAGS) clean ) |
|
||||
|
|
||||
uninstall-local: |
|
||||
( cd doc && $(MAKE) $(AM_MAKEFLAGS) uninstall ) |
|
||||
|
|
||||
|
|
||||
# We check this every time make is run, with configure.ac being touched to
|
|
||||
# trigger an update of the build system files if update_version changes the
|
|
||||
# current PACKAGE_VERSION (or if package_version was modified manually by a
|
|
||||
# user with either AUTO_UPDATE=no or no update_version script present - the
|
|
||||
# latter being the normal case for tarball releases).
|
|
||||
#
|
|
||||
# We can't just add the package_version file to CONFIGURE_DEPENDENCIES since
|
|
||||
# simply running autoconf will not actually regenerate configure for us when
|
|
||||
# the content of that file changes (due to autoconf dependency checking not
|
|
||||
# knowing about that without us creating yet another file for it to include).
|
|
||||
#
|
|
||||
# The MAKECMDGOALS check is a gnu-make'ism, but will degrade 'gracefully' for
|
|
||||
# makes that don't support it. The only loss of functionality is not forcing
|
|
||||
# an update of package_version for `make dist` if AUTO_UPDATE=no, but that is
|
|
||||
# unlikely to be a real problem for any real user.
|
|
||||
$(top_srcdir)/configure.ac: force |
|
||||
@case "$(MAKECMDGOALS)" in \
|
|
||||
dist-hook) exit 0 ;; \
|
|
||||
dist-* | dist | distcheck | distclean) _arg=release ;; \
|
|
||||
esac; \
|
|
||||
if ! $(top_srcdir)/update_version $$_arg 2> /dev/null; then \
|
|
||||
if [ ! -e $(top_srcdir)/package_version ]; then \
|
|
||||
echo 'PACKAGE_VERSION="unknown"' > $(top_srcdir)/package_version; \
|
|
||||
fi; \
|
|
||||
. $(top_srcdir)/package_version || exit 1; \
|
|
||||
[ "$(PACKAGE_VERSION)" != "$$PACKAGE_VERSION" ] || exit 0; \
|
|
||||
fi; \
|
|
||||
touch $@ |
|
||||
|
|
||||
force: |
|
||||
|
|
||||
# Create a minimal package_version file when make dist is run.
|
|
||||
dist-hook: |
|
||||
echo 'PACKAGE_VERSION="$(PACKAGE_VERSION)"' > $(top_distdir)/package_version |
|
||||
|
|
||||
|
|
||||
.PHONY: opus check-opus install-opus docs install-docs |
|
||||
|
|
||||
# automake doesn't do dependency tracking for asm files, that I can tell
|
|
||||
$(CELT_SOURCES_ARM_ASM:%.s=%-gnu.S): celt/arm/armopts-gnu.S |
|
||||
$(CELT_SOURCES_ARM_ASM:%.s=%-gnu.S): $(top_srcdir)/celt/arm/arm2gnu.pl |
|
||||
|
|
||||
# convert ARM asm to GNU as format
|
|
||||
%-gnu.S: $(top_srcdir)/%.s |
|
||||
$(top_srcdir)/celt/arm/arm2gnu.pl @ARM2GNU_PARAMS@ < $< > $@ |
|
||||
# For autoconf-modified sources (e.g., armopts.s)
|
|
||||
%-gnu.S: %.s |
|
||||
$(top_srcdir)/celt/arm/arm2gnu.pl @ARM2GNU_PARAMS@ < $< > $@ |
|
||||
|
|
||||
OPT_UNIT_TEST_OBJ = $(celt_tests_test_unit_mathops_SOURCES:.c=.o) \
|
|
||||
$(celt_tests_test_unit_rotation_SOURCES:.c=.o) \
|
|
||||
$(celt_tests_test_unit_mdct_SOURCES:.c=.o) \
|
|
||||
$(celt_tests_test_unit_dft_SOURCES:.c=.o) \
|
|
||||
$(silk_tests_test_unit_LPC_inv_pred_gain_SOURCES:.c=.o) |
|
||||
|
|
||||
if HAVE_SSE |
|
||||
SSE_OBJ = $(CELT_SOURCES_SSE:.c=.lo) |
|
||||
$(SSE_OBJ): CFLAGS += $(OPUS_X86_SSE_CFLAGS) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_SSE2 |
|
||||
SSE2_OBJ = $(CELT_SOURCES_SSE2:.c=.lo) \
|
|
||||
$(DNN_SOURCES_SSE2:.c=.lo) |
|
||||
$(SSE2_OBJ): CFLAGS += $(OPUS_X86_SSE2_CFLAGS) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_SSE4_1 |
|
||||
SSE4_1_OBJ = $(CELT_SOURCES_SSE4_1:.c=.lo) \
|
|
||||
$(DNN_SOURCES_SSE4_1:.c=.lo) \
|
|
||||
$(SILK_SOURCES_SSE4_1:.c=.lo) \
|
|
||||
$(SILK_SOURCES_FIXED_SSE4_1:.c=.lo) |
|
||||
$(SSE4_1_OBJ): CFLAGS += $(OPUS_X86_SSE4_1_CFLAGS) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_AVX2 |
|
||||
AVX2_OBJ = $(CELT_SOURCES_AVX2:.c=.lo) \
|
|
||||
$(SILK_SOURCES_AVX2:.c=.lo) \
|
|
||||
$(SILK_SOURCES_FLOAT_AVX2:.c=.lo) \
|
|
||||
$(DNN_SOURCES_AVX2:.c=.lo) |
|
||||
$(AVX2_OBJ): CFLAGS += $(OPUS_X86_AVX2_CFLAGS) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_ARM_NEON_INTR |
|
||||
ARM_NEON_INTR_OBJ = $(CELT_SOURCES_ARM_NEON_INTR:.c=.lo) \
|
|
||||
$(SILK_SOURCES_ARM_NEON_INTR:.c=.lo) \
|
|
||||
$(DNN_SOURCES_NEON:.c=.lo) \
|
|
||||
$(SILK_SOURCES_FIXED_ARM_NEON_INTR:.c=.lo) |
|
||||
$(ARM_NEON_INTR_OBJ): CFLAGS += \ |
|
||||
$(OPUS_ARM_NEON_INTR_CFLAGS) $(NE10_CFLAGS) |
|
||||
endif |
|
||||
|
|
||||
if HAVE_ARM_DOTPROD |
|
||||
ARM_DOTPROD_OBJ = $(DNN_SOURCES_DOTPROD:.c=.lo) |
|
||||
$(ARM_DOTPROD_OBJ): CFLAGS += $(ARM_DOTPROD_INTR_CFLAGS) |
|
||||
endif |
|
||||
File diff suppressed because it is too large
@ -1,169 +0,0 @@ |
|||||
#################### COMPILE OPTIONS #######################
|
|
||||
|
|
||||
# Uncomment this for fixed-point build
|
|
||||
FIXED_POINT=1 |
|
||||
|
|
||||
# It is strongly recommended to uncomment one of these
|
|
||||
# VAR_ARRAYS: Use C99 variable-length arrays for stack allocation
|
|
||||
# USE_ALLOCA: Use alloca() for stack allocation
|
|
||||
# If none is defined, then the fallback is a non-threadsafe global array
|
|
||||
CFLAGS := -DUSE_ALLOCA $(CFLAGS) |
|
||||
#CFLAGS := -DVAR_ARRAYS $(CFLAGS)
|
|
||||
|
|
||||
# These options affect performance
|
|
||||
# HAVE_LRINTF: Use C99 intrinsics to speed up float-to-int conversion
|
|
||||
CFLAGS := -DHAVE_LRINTF $(CFLAGS) |
|
||||
|
|
||||
###################### END OF OPTIONS ######################
|
|
||||
|
|
||||
-include package_version |
|
||||
|
|
||||
include silk_sources.mk |
|
||||
include celt_sources.mk |
|
||||
include opus_sources.mk |
|
||||
|
|
||||
ifdef FIXED_POINT |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FIXED) |
|
||||
else |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FLOAT) |
|
||||
OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) |
|
||||
endif |
|
||||
|
|
||||
EXESUFFIX = |
|
||||
LIBPREFIX = lib |
|
||||
LIBSUFFIX = .a |
|
||||
OBJSUFFIX = .o |
|
||||
|
|
||||
CC = $(TOOLCHAIN_PREFIX)cc$(TOOLCHAIN_SUFFIX) |
|
||||
AR = $(TOOLCHAIN_PREFIX)ar |
|
||||
RANLIB = $(TOOLCHAIN_PREFIX)ranlib |
|
||||
CP = $(TOOLCHAIN_PREFIX)cp |
|
||||
|
|
||||
cppflags-from-defines = $(addprefix -D,$(1)) |
|
||||
cppflags-from-includes = $(addprefix -I,$(1)) |
|
||||
ldflags-from-ldlibdirs = $(addprefix -L,$(1)) |
|
||||
ldlibs-from-libs = $(addprefix -l,$(1)) |
|
||||
|
|
||||
WARNINGS = -Wall -W -Wstrict-prototypes -Wextra -Wcast-align -Wnested-externs -Wshadow |
|
||||
|
|
||||
CFLAGS += -mips32r2 -mno-mips16 -std=gnu99 -O2 -g $(WARNINGS) -DENABLE_ASSERTIONS -DMIPSr1_ASM -DOPUS_BUILD -mdspr2 -march=74kc -mtune=74kc -mmt -mgp32 |
|
||||
|
|
||||
CINCLUDES = include silk celt |
|
||||
|
|
||||
ifdef FIXED_POINT |
|
||||
CFLAGS += -DFIXED_POINT=1 -DDISABLE_FLOAT_API |
|
||||
CINCLUDES += silk/fixed |
|
||||
else |
|
||||
CINCLUDES += silk/float |
|
||||
endif |
|
||||
|
|
||||
|
|
||||
LIBS = m |
|
||||
|
|
||||
LDLIBDIRS = ./ |
|
||||
|
|
||||
CFLAGS += $(call cppflags-from-defines,$(CDEFINES)) |
|
||||
CFLAGS += $(call cppflags-from-includes,$(CINCLUDES)) |
|
||||
LDFLAGS += $(call ldflags-from-ldlibdirs,$(LDLIBDIRS)) |
|
||||
LDLIBS += $(call ldlibs-from-libs,$(LIBS)) |
|
||||
|
|
||||
COMPILE.c.cmdline = $(CC) -c $(CFLAGS) -o $@ $< |
|
||||
LINK.o = $(CC) $(LDPREFLAGS) $(LDFLAGS) |
|
||||
LINK.o.cmdline = $(LINK.o) $^ $(LDLIBS) -o $@$(EXESUFFIX) |
|
||||
|
|
||||
ARCHIVE.cmdline = $(AR) $(ARFLAGS) $@ $^ && $(RANLIB) $@ |
|
||||
|
|
||||
%$(OBJSUFFIX):%.c |
|
||||
$(COMPILE.c.cmdline) |
|
||||
|
|
||||
%$(OBJSUFFIX):%.cpp |
|
||||
$(COMPILE.cpp.cmdline) |
|
||||
|
|
||||
# Directives
|
|
||||
|
|
||||
|
|
||||
# Variable definitions
|
|
||||
LIB_NAME = opus |
|
||||
TARGET = $(LIBPREFIX)$(LIB_NAME)$(LIBSUFFIX) |
|
||||
|
|
||||
SRCS_C = $(SILK_SOURCES) $(CELT_SOURCES) $(OPUS_SOURCES) |
|
||||
|
|
||||
OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(SRCS_C)) |
|
||||
|
|
||||
OPUSDEMO_SRCS_C = src/opus_demo.c |
|
||||
OPUSDEMO_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSDEMO_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSAPI_SRCS_C = tests/test_opus_api.c |
|
||||
TESTOPUSAPI_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSAPI_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSDECODE_SRCS_C = tests/test_opus_decode.c |
|
||||
TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c tests/opus_encode_regressions.c |
|
||||
TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSEXTENSIONS_SRCS_C = tests/test_opus_extensions.c |
|
||||
TESTOPUSEXTENSIONS_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSEXTENSIONS_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c |
|
||||
TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C)) |
|
||||
|
|
||||
OPUSCOMPARE_SRCS_C = src/opus_compare.c |
|
||||
OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C)) |
|
||||
|
|
||||
TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_extensions test_opus_padding |
|
||||
|
|
||||
# Rules
|
|
||||
all: lib opus_demo opus_compare $(TESTS) |
|
||||
|
|
||||
lib: $(TARGET) |
|
||||
|
|
||||
check: all |
|
||||
for test in $(TESTS); do ./$$test; done |
|
||||
|
|
||||
$(TARGET): $(OBJS) |
|
||||
$(ARCHIVE.cmdline) |
|
||||
|
|
||||
opus_demo$(EXESUFFIX): $(OPUSDEMO_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_api$(EXESUFFIX): $(TESTOPUSAPI_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_extensions$(EXESUFFIX): $(TESTOPUSEXTENSIONS_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
opus_compare$(EXESUFFIX): $(OPUSCOMPARE_OBJS) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
celt/celt.o: CFLAGS += -DPACKAGE_VERSION='$(PACKAGE_VERSION)' |
|
||||
celt/celt.o: package_version |
|
||||
|
|
||||
package_version: force |
|
||||
@if [ -x ./update_version ]; then \
|
|
||||
./update_version || true; \
|
|
||||
elif [ ! -e ./package_version ]; then \
|
|
||||
echo 'PACKAGE_VERSION="unknown"' > ./package_version; \
|
|
||||
fi |
|
||||
|
|
||||
force: |
|
||||
|
|
||||
clean: |
|
||||
rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \
|
|
||||
test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \
|
|
||||
test_opus_encode$(EXESUFFIX) test_opus_extensions$(EXESUFFIX) \
|
|
||||
test_opus_padding$(EXESUFFIX) |
|
||||
$(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \
|
|
||||
$(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) \
|
|
||||
$(TESTOPUSEXTENSIONS_OBJS) $(TESTOPUSPADDING_OBJS) |
|
||||
|
|
||||
.PHONY: all lib clean force check |
|
||||
@ -1,167 +0,0 @@ |
|||||
#################### COMPILE OPTIONS #######################
|
|
||||
|
|
||||
# Uncomment this for fixed-point build
|
|
||||
#FIXED_POINT=1
|
|
||||
|
|
||||
# It is strongly recommended to uncomment one of these
|
|
||||
# VAR_ARRAYS: Use C99 variable-length arrays for stack allocation
|
|
||||
# USE_ALLOCA: Use alloca() for stack allocation
|
|
||||
# If none is defined, then the fallback is a non-threadsafe global array
|
|
||||
CFLAGS := -DUSE_ALLOCA $(CFLAGS) |
|
||||
#CFLAGS := -DVAR_ARRAYS $(CFLAGS)
|
|
||||
|
|
||||
# These options affect performance
|
|
||||
# HAVE_LRINTF: Use C99 intrinsics to speed up float-to-int conversion
|
|
||||
#CFLAGS := -DHAVE_LRINTF $(CFLAGS)
|
|
||||
|
|
||||
###################### END OF OPTIONS ######################
|
|
||||
|
|
||||
-include package_version |
|
||||
|
|
||||
include silk_sources.mk |
|
||||
include celt_sources.mk |
|
||||
include opus_sources.mk |
|
||||
|
|
||||
ifdef FIXED_POINT |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FIXED) |
|
||||
else |
|
||||
SILK_SOURCES += $(SILK_SOURCES_FLOAT) |
|
||||
OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) |
|
||||
endif |
|
||||
|
|
||||
EXESUFFIX = |
|
||||
LIBPREFIX = lib |
|
||||
LIBSUFFIX = .a |
|
||||
OBJSUFFIX = .o |
|
||||
|
|
||||
CC = $(TOOLCHAIN_PREFIX)cc$(TOOLCHAIN_SUFFIX) |
|
||||
AR = $(TOOLCHAIN_PREFIX)ar |
|
||||
RANLIB = $(TOOLCHAIN_PREFIX)ranlib |
|
||||
CP = $(TOOLCHAIN_PREFIX)cp |
|
||||
|
|
||||
cppflags-from-defines = $(addprefix -D,$(1)) |
|
||||
cppflags-from-includes = $(addprefix -I,$(1)) |
|
||||
ldflags-from-ldlibdirs = $(addprefix -L,$(1)) |
|
||||
ldlibs-from-libs = $(addprefix -l,$(1)) |
|
||||
|
|
||||
WARNINGS = -Wall -W -Wstrict-prototypes -Wextra -Wcast-align -Wnested-externs -Wshadow |
|
||||
CFLAGS += -O2 -g $(WARNINGS) -DOPUS_BUILD |
|
||||
CINCLUDES = include silk celt |
|
||||
|
|
||||
ifdef FIXED_POINT |
|
||||
CFLAGS += -DFIXED_POINT=1 -DDISABLE_FLOAT_API |
|
||||
CINCLUDES += silk/fixed |
|
||||
else |
|
||||
CINCLUDES += silk/float |
|
||||
endif |
|
||||
|
|
||||
|
|
||||
LIBS = m |
|
||||
|
|
||||
LDLIBDIRS = ./ |
|
||||
|
|
||||
CFLAGS += $(call cppflags-from-defines,$(CDEFINES)) |
|
||||
CFLAGS += $(call cppflags-from-includes,$(CINCLUDES)) |
|
||||
LDFLAGS += $(call ldflags-from-ldlibdirs,$(LDLIBDIRS)) |
|
||||
LDLIBS += $(call ldlibs-from-libs,$(LIBS)) |
|
||||
|
|
||||
COMPILE.c.cmdline = $(CC) -c $(CFLAGS) -o $@ $< |
|
||||
LINK.o = $(CC) $(LDPREFLAGS) $(LDFLAGS) |
|
||||
LINK.o.cmdline = $(LINK.o) $^ $(LDLIBS) -o $@$(EXESUFFIX) |
|
||||
|
|
||||
ARCHIVE.cmdline = $(AR) $(ARFLAGS) $@ $^ && $(RANLIB) $@ |
|
||||
|
|
||||
%$(OBJSUFFIX):%.c |
|
||||
$(COMPILE.c.cmdline) |
|
||||
|
|
||||
%$(OBJSUFFIX):%.cpp |
|
||||
$(COMPILE.cpp.cmdline) |
|
||||
|
|
||||
# Directives
|
|
||||
|
|
||||
|
|
||||
# Variable definitions
|
|
||||
LIB_NAME = opus |
|
||||
TARGET = $(LIBPREFIX)$(LIB_NAME)$(LIBSUFFIX) |
|
||||
|
|
||||
SRCS_C = $(SILK_SOURCES) $(CELT_SOURCES) $(OPUS_SOURCES) |
|
||||
|
|
||||
OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(SRCS_C)) |
|
||||
|
|
||||
OPUSDEMO_SRCS_C = src/opus_demo.c |
|
||||
OPUSDEMO_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSDEMO_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSAPI_SRCS_C = tests/test_opus_api.c |
|
||||
TESTOPUSAPI_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSAPI_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSDECODE_SRCS_C = tests/test_opus_decode.c |
|
||||
TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c tests/opus_encode_regressions.c |
|
||||
TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSEXTENSIONS_SRCS_C = tests/test_opus_extensions.c |
|
||||
TESTOPUSEXTENSIONS_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSEXTENSIONS_SRCS_C)) |
|
||||
|
|
||||
TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c |
|
||||
TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C)) |
|
||||
|
|
||||
OPUSCOMPARE_SRCS_C = src/opus_compare.c |
|
||||
OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C)) |
|
||||
|
|
||||
TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_extensions test_opus_padding |
|
||||
|
|
||||
# Rules
|
|
||||
all: lib opus_demo opus_compare $(TESTS) |
|
||||
|
|
||||
lib: $(TARGET) |
|
||||
|
|
||||
check: all |
|
||||
for test in $(TESTS); do ./$$test; done |
|
||||
|
|
||||
$(TARGET): $(OBJS) |
|
||||
$(ARCHIVE.cmdline) |
|
||||
|
|
||||
opus_demo$(EXESUFFIX): $(OPUSDEMO_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_api$(EXESUFFIX): $(TESTOPUSAPI_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_extensions$(EXESUFFIX): $(TESTOPUSEXTENSIONS_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
opus_compare$(EXESUFFIX): $(OPUSCOMPARE_OBJS) |
|
||||
$(LINK.o.cmdline) |
|
||||
|
|
||||
celt/celt.o: CFLAGS += -DPACKAGE_VERSION='$(PACKAGE_VERSION)' |
|
||||
celt/celt.o: package_version |
|
||||
|
|
||||
package_version: force |
|
||||
@if [ -x ./update_version ]; then \
|
|
||||
./update_version || true; \
|
|
||||
elif [ ! -e ./package_version ]; then \
|
|
||||
echo 'PACKAGE_VERSION="unknown"' > ./package_version; \
|
|
||||
fi |
|
||||
|
|
||||
force: |
|
||||
|
|
||||
clean: |
|
||||
rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \
|
|
||||
test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \
|
|
||||
test_opus_encode$(EXESUFFIX) test_opus_extensions$(EXESUFFIX) \
|
|
||||
test_opus_padding$(EXESUFFIX) |
|
||||
$(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \
|
|
||||
$(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) \
|
|
||||
$(TESTOPUSEXTENSIONS_OBJS) $(TESTOPUSPADDING_OBJS) |
|
||||
|
|
||||
.PHONY: all lib clean force check |
|
||||
@ -1,189 +0,0 @@ |
|||||
== Opus audio codec == |
|
||||
|
|
||||
Opus is a codec for interactive speech and audio transmission over the Internet. |
|
||||
|
|
||||
Opus can 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. |
|
||||
|
|
||||
Opus, when coupled with an appropriate container format, is also suitable |
|
||||
for non-realtime stored-file applications such as music distribution, game |
|
||||
soundtracks, portable music players, jukeboxes, and other applications that |
|
||||
have historically used high latency formats such as MP3, AAC, or Vorbis. |
|
||||
|
|
||||
Opus is specified by IETF RFC 6716: |
|
||||
https://tools.ietf.org/html/rfc6716 |
|
||||
|
|
||||
The Opus format and this implementation of it are subject to the royalty- |
|
||||
free patent and copyright licenses specified in the file COPYING. |
|
||||
|
|
||||
This package implements a shared library for encoding and decoding raw Opus |
|
||||
bitstreams. Raw Opus bitstreams should be used over RTP according to |
|
||||
https://tools.ietf.org/html/rfc7587 |
|
||||
|
|
||||
The package also includes a number of test tools used for testing the |
|
||||
correct operation of the library. The bitstreams read/written by these |
|
||||
tools should not be used for Opus file distribution: They include |
|
||||
additional debugging data and cannot support seeking. |
|
||||
|
|
||||
Opus stored in files should use the Ogg encapsulation for Opus which is |
|
||||
described at: |
|
||||
https://tools.ietf.org/html/rfc7845 |
|
||||
|
|
||||
An opus-tools package is available which provides encoding and decoding of |
|
||||
Ogg encapsulated Opus files and includes a number of useful features. |
|
||||
|
|
||||
Opus-tools can be found at: |
|
||||
https://gitlab.xiph.org/xiph/opus-tools.git |
|
||||
or on the main Opus website: |
|
||||
https://opus-codec.org/ |
|
||||
|
|
||||
== Deep Learning and Opus == |
|
||||
|
|
||||
Lossy networks continue to be a challenge for real-time communications. |
|
||||
While the original implementation of Opus provides an excellent packet loss |
|
||||
concealment mechanism, the team has continued to advance the methodology used |
|
||||
to improve audio quality in challenge network environments. |
|
||||
|
|
||||
In Opus 1.5, we added a deep learning based redundancy encoder that enhances |
|
||||
audio in lossy networks by embedding one second of recovery data in the padding |
|
||||
data of each packet. The underlying algorithm behind encoding and decoding the |
|
||||
recovery data is called the deep redundancy (DRED) algorithm. By leveraging |
|
||||
the padding data within the packet, Opus 1.5 is fully backward compatible with |
|
||||
prior revisions of Opus. Please see the README under the "dnn" subdirectory to |
|
||||
understand DRED. |
|
||||
|
|
||||
DRED was developed by a team that Amazon Web Services initially sponsored, |
|
||||
who open-sourced the implementation as well as began the |
|
||||
standardization process at the IETF: |
|
||||
https://datatracker.ietf.org/doc/draft-ietf-mlcodec-opus-extension/ |
|
||||
The license behind Opus or the intellectual property position of Opus does |
|
||||
not change with Opus 1.5. |
|
||||
|
|
||||
== Compiling libopus == |
|
||||
|
|
||||
To build from a distribution tarball, you only need to do the following: |
|
||||
|
|
||||
% ./configure |
|
||||
% make |
|
||||
|
|
||||
To build from the git repository, the following steps are necessary: |
|
||||
|
|
||||
0) Set up a development environment: |
|
||||
|
|
||||
On an Ubuntu or Debian family Linux distribution: |
|
||||
|
|
||||
% sudo apt-get install git autoconf automake libtool gcc make |
|
||||
|
|
||||
On a Fedora/Redhat based Linux: |
|
||||
|
|
||||
% sudo dnf install git autoconf automake libtool gcc make |
|
||||
|
|
||||
Or for older Redhat/Centos Linux releases: |
|
||||
|
|
||||
% sudo yum install git autoconf automake libtool gcc make |
|
||||
|
|
||||
On Apple macOS, install Xcode and brew.sh, then in the Terminal enter: |
|
||||
|
|
||||
% brew install autoconf automake libtool |
|
||||
|
|
||||
1) Clone the repository: |
|
||||
|
|
||||
% git clone https://gitlab.xiph.org/xiph/opus.git |
|
||||
% cd opus |
|
||||
|
|
||||
2) Compiling the source |
|
||||
|
|
||||
% ./autogen.sh |
|
||||
% ./configure |
|
||||
% make |
|
||||
|
|
||||
On x86, it's a good idea to use a -march= option that allows the use of AVX2. |
|
||||
|
|
||||
3) Install the codec libraries (optional) |
|
||||
|
|
||||
% sudo make install |
|
||||
|
|
||||
Once you have compiled the codec, there will be a opus_demo executable |
|
||||
in the top directory. |
|
||||
|
|
||||
Usage: opus_demo [-e] <application> <sampling rate (Hz)> <channels (1/2)> |
|
||||
<bits per second> [options] <input> <output> |
|
||||
opus_demo -d <sampling rate (Hz)> <channels (1/2)> [options] |
|
||||
<input> <output> |
|
||||
|
|
||||
mode: voip | audio | restricted-lowdelay |
|
||||
options: |
|
||||
-e : only runs the encoder (output the bit-stream) |
|
||||
-d : only runs the decoder (reads the bit-stream as input) |
|
||||
-cbr : enable constant bitrate; default: variable bitrate |
|
||||
-cvbr : enable constrained variable bitrate; default: |
|
||||
unconstrained |
|
||||
-bandwidth <NB|MB|WB|SWB|FB> |
|
||||
: audio bandwidth (from narrowband to fullband); |
|
||||
default: sampling rate |
|
||||
-framesize <2.5|5|10|20|40|60> |
|
||||
: frame size in ms; default: 20 |
|
||||
-max_payload <bytes> |
|
||||
: maximum payload size in bytes, default: 1024 |
|
||||
-complexity <comp> |
|
||||
: complexity, 0 (lowest) ... 10 (highest); default: 10 |
|
||||
-inbandfec : enable SILK inband FEC |
|
||||
-forcemono : force mono encoding, even for stereo input |
|
||||
-dtx : enable SILK DTX |
|
||||
-loss <perc> : simulate packet loss, in percent (0-100); default: 0 |
|
||||
|
|
||||
input and output are little-endian signed 16-bit PCM files or opus |
|
||||
bitstreams with simple opus_demo proprietary framing. |
|
||||
|
|
||||
== Testing == |
|
||||
|
|
||||
This package includes a collection of automated unit and system tests |
|
||||
which SHOULD be run after compiling the package especially the first |
|
||||
time it is run on a new platform. |
|
||||
|
|
||||
To run the integrated tests: |
|
||||
|
|
||||
% make check |
|
||||
|
|
||||
There is also collection of standard test vectors which are not |
|
||||
included in this package for size reasons but can be obtained from: |
|
||||
https://opus-codec.org/docs/opus_testvectors-rfc8251.tar.gz |
|
||||
|
|
||||
To run compare the code to these test vectors: |
|
||||
|
|
||||
% curl -OL https://opus-codec.org/docs/opus_testvectors-rfc8251.tar.gz |
|
||||
% tar -zxf opus_testvectors-rfc8251.tar.gz |
|
||||
% ./tests/run_vectors.sh ./ opus_newvectors 48000 |
|
||||
|
|
||||
== Compiling libopus for Windows and alternative build systems == |
|
||||
|
|
||||
See cmake/README.md or meson/README.md. |
|
||||
|
|
||||
== Portability notes == |
|
||||
|
|
||||
This implementation uses floating-point by default but can be compiled to |
|
||||
use only fixed-point arithmetic by setting --enable-fixed-point (if using |
|
||||
autoconf) or by defining the FIXED_POINT macro (if building manually). |
|
||||
The fixed point implementation has somewhat lower audio quality and is |
|
||||
slower on platforms with fast FPUs, it is normally only used in embedded |
|
||||
environments. |
|
||||
|
|
||||
The implementation can be compiled with either a C89 or a C99 compiler. |
|
||||
While it does not rely on any _undefined behavior_ as defined by C89 or |
|
||||
C99, it relies on common _implementation-defined behavior_ for two's |
|
||||
complement architectures: |
|
||||
|
|
||||
o Right shifts of negative values are consistent with two's |
|
||||
complement arithmetic, so that a>>b is equivalent to |
|
||||
floor(a/(2^b)), |
|
||||
|
|
||||
o For conversion to a signed integer of N bits, the value is reduced |
|
||||
modulo 2^N to be within range of the type, |
|
||||
|
|
||||
o The result of integer division of a negative value is truncated |
|
||||
towards zero, and |
|
||||
|
|
||||
o The compiler provides a 64-bit integer type (a C99 requirement |
|
||||
which is supported by most C89 compilers). |
|
||||
File diff suppressed because it is too large
@ -1,182 +0,0 @@ |
|||||
/*Copyright (c) 2003-2004, Mark Borgerding
|
|
||||
|
|
||||
All rights reserved. |
|
||||
|
|
||||
Redistribution and use in source and binary forms, with or without |
|
||||
modification, are permitted provided that the following conditions are met: |
|
||||
|
|
||||
* Redistributions of source code must retain the above copyright notice, |
|
||||
this list of conditions and the following disclaimer. |
|
||||
* Redistributions in binary form must reproduce the above copyright notice, |
|
||||
this list of conditions and the following disclaimer in the |
|
||||
documentation and/or other materials provided with the distribution. |
|
||||
|
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
POSSIBILITY OF SUCH DAMAGE.*/ |
|
||||
|
|
||||
#ifndef KISS_FFT_GUTS_H |
|
||||
#define KISS_FFT_GUTS_H |
|
||||
|
|
||||
#define MIN(a,b) ((a)<(b) ? (a):(b)) |
|
||||
#define MAX(a,b) ((a)>(b) ? (a):(b)) |
|
||||
|
|
||||
/* kiss_fft.h
|
|
||||
defines kiss_fft_scalar as either short or a float type |
|
||||
and defines |
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ |
|
||||
#include "kiss_fft.h" |
|
||||
|
|
||||
/*
|
|
||||
Explanation of macros dealing with complex math: |
|
||||
|
|
||||
C_MUL(m,a,b) : m = a*b |
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise |
|
||||
C_SUB( res, a,b) : res = a - b |
|
||||
C_SUBFROM( res , a) : res -= a |
|
||||
C_ADDTO( res , a) : res += a |
|
||||
* */ |
|
||||
#ifdef FIXED_POINT |
|
||||
#include "arch.h" |
|
||||
|
|
||||
|
|
||||
#define SAMP_MAX 2147483647 |
|
||||
#define TWID_MAX 32767 |
|
||||
#define TRIG_UPSCALE 1 |
|
||||
|
|
||||
#define SAMP_MIN -SAMP_MAX |
|
||||
|
|
||||
|
|
||||
# define S_MUL(a,b) MULT16_32_Q15(b, a) |
|
||||
|
|
||||
# define C_MUL(m,a,b) \ |
|
||||
do{ (m).r = SUB32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ |
|
||||
(m).i = ADD32_ovflw(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0) |
|
||||
|
|
||||
# define C_MULC(m,a,b) \ |
|
||||
do{ (m).r = ADD32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ |
|
||||
(m).i = SUB32_ovflw(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0) |
|
||||
|
|
||||
# define C_MULBYSCALAR( c, s ) \ |
|
||||
do{ (c).r = S_MUL( (c).r , s ) ;\ |
|
||||
(c).i = S_MUL( (c).i , s ) ; }while(0) |
|
||||
|
|
||||
# define DIVSCALAR(x,k) \ |
|
||||
(x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 ) |
|
||||
|
|
||||
# define C_FIXDIV(c,div) \ |
|
||||
do { DIVSCALAR( (c).r , div); \ |
|
||||
DIVSCALAR( (c).i , div); }while (0) |
|
||||
|
|
||||
#define C_ADD( res, a,b)\ |
|
||||
do {(res).r=ADD32_ovflw((a).r,(b).r); (res).i=ADD32_ovflw((a).i,(b).i); \ |
|
||||
}while(0) |
|
||||
#define C_SUB( res, a,b)\ |
|
||||
do {(res).r=SUB32_ovflw((a).r,(b).r); (res).i=SUB32_ovflw((a).i,(b).i); \ |
|
||||
}while(0) |
|
||||
#define C_ADDTO( res , a)\ |
|
||||
do {(res).r = ADD32_ovflw((res).r, (a).r); (res).i = ADD32_ovflw((res).i,(a).i);\ |
|
||||
}while(0) |
|
||||
|
|
||||
#define C_SUBFROM( res , a)\ |
|
||||
do {(res).r = ADD32_ovflw((res).r,(a).r); (res).i = SUB32_ovflw((res).i,(a).i); \ |
|
||||
}while(0) |
|
||||
|
|
||||
#if defined(OPUS_ARM_INLINE_ASM) |
|
||||
#include "arm/kiss_fft_armv4.h" |
|
||||
#endif |
|
||||
|
|
||||
#if defined(OPUS_ARM_INLINE_EDSP) |
|
||||
#include "arm/kiss_fft_armv5e.h" |
|
||||
#endif |
|
||||
#if defined(MIPSr1_ASM) |
|
||||
#include "mips/kiss_fft_mipsr1.h" |
|
||||
#endif |
|
||||
|
|
||||
#else /* not FIXED_POINT*/ |
|
||||
|
|
||||
# define S_MUL(a,b) ( (a)*(b) ) |
|
||||
#define C_MUL(m,a,b) \ |
|
||||
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ |
|
||||
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0) |
|
||||
#define C_MULC(m,a,b) \ |
|
||||
do{ (m).r = (a).r*(b).r + (a).i*(b).i;\ |
|
||||
(m).i = (a).i*(b).r - (a).r*(b).i; }while(0) |
|
||||
|
|
||||
#define C_MUL4(m,a,b) C_MUL(m,a,b) |
|
||||
|
|
||||
# define C_FIXDIV(c,div) /* NOOP */ |
|
||||
# define C_MULBYSCALAR( c, s ) \ |
|
||||
do{ (c).r *= (s);\ |
|
||||
(c).i *= (s); }while(0) |
|
||||
#endif |
|
||||
|
|
||||
#ifndef CHECK_OVERFLOW_OP |
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */ |
|
||||
#endif |
|
||||
|
|
||||
#ifndef C_ADD |
|
||||
#define C_ADD( res, a,b)\ |
|
||||
do { \ |
|
||||
CHECK_OVERFLOW_OP((a).r,+,(b).r)\ |
|
||||
CHECK_OVERFLOW_OP((a).i,+,(b).i)\ |
|
||||
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ |
|
||||
}while(0) |
|
||||
#define C_SUB( res, a,b)\ |
|
||||
do { \ |
|
||||
CHECK_OVERFLOW_OP((a).r,-,(b).r)\ |
|
||||
CHECK_OVERFLOW_OP((a).i,-,(b).i)\ |
|
||||
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ |
|
||||
}while(0) |
|
||||
#define C_ADDTO( res , a)\ |
|
||||
do { \ |
|
||||
CHECK_OVERFLOW_OP((res).r,+,(a).r)\ |
|
||||
CHECK_OVERFLOW_OP((res).i,+,(a).i)\ |
|
||||
(res).r += (a).r; (res).i += (a).i;\ |
|
||||
}while(0) |
|
||||
|
|
||||
#define C_SUBFROM( res , a)\ |
|
||||
do {\ |
|
||||
CHECK_OVERFLOW_OP((res).r,-,(a).r)\ |
|
||||
CHECK_OVERFLOW_OP((res).i,-,(a).i)\ |
|
||||
(res).r -= (a).r; (res).i -= (a).i; \ |
|
||||
}while(0) |
|
||||
#endif /* C_ADD defined */ |
|
||||
|
|
||||
#ifdef FIXED_POINT |
|
||||
/*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase))))
|
|
||||
# define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/ |
|
||||
# define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase)) |
|
||||
# define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase)) |
|
||||
# define HALF_OF(x) ((x)>>1) |
|
||||
#elif defined(USE_SIMD) |
|
||||
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) |
|
||||
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) |
|
||||
# define HALF_OF(x) ((x)*_mm_set1_ps(.5f)) |
|
||||
#else |
|
||||
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) |
|
||||
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) |
|
||||
# define HALF_OF(x) ((x)*.5f) |
|
||||
#endif |
|
||||
|
|
||||
#define kf_cexp(x,phase) \ |
|
||||
do{ \ |
|
||||
(x)->r = KISS_FFT_COS(phase);\ |
|
||||
(x)->i = KISS_FFT_SIN(phase);\ |
|
||||
}while(0) |
|
||||
|
|
||||
#define kf_cexp2(x,phase) \ |
|
||||
do{ \ |
|
||||
(x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\ |
|
||||
(x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\ |
|
||||
}while(0) |
|
||||
|
|
||||
#endif /* KISS_FFT_GUTS_H */ |
|
||||
@ -1,291 +0,0 @@ |
|||||
/* Copyright (c) 2003-2008 Jean-Marc Valin
|
|
||||
Copyright (c) 2007-2008 CSIRO |
|
||||
Copyright (c) 2007-2009 Xiph.Org Foundation |
|
||||
Written by Jean-Marc Valin */ |
|
||||
/**
|
|
||||
@file arch.h |
|
||||
@brief Various architecture definitions for CELT |
|
||||
*/ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef ARCH_H |
|
||||
#define ARCH_H |
|
||||
|
|
||||
#include "opus_types.h" |
|
||||
#include "opus_defines.h" |
|
||||
|
|
||||
# if !defined(__GNUC_PREREQ) |
|
||||
# if defined(__GNUC__)&&defined(__GNUC_MINOR__) |
|
||||
# define __GNUC_PREREQ(_maj,_min) \ |
|
||||
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) |
|
||||
# else |
|
||||
# define __GNUC_PREREQ(_maj,_min) 0 |
|
||||
# endif |
|
||||
# endif |
|
||||
|
|
||||
#if OPUS_GNUC_PREREQ(3, 0) |
|
||||
#define opus_likely(x) (__builtin_expect(!!(x), 1)) |
|
||||
#define opus_unlikely(x) (__builtin_expect(!!(x), 0)) |
|
||||
#else |
|
||||
#define opus_likely(x) (!!(x)) |
|
||||
#define opus_unlikely(x) (!!(x)) |
|
||||
#endif |
|
||||
|
|
||||
#define CELT_SIG_SCALE 32768.f |
|
||||
|
|
||||
#define CELT_FATAL(str) celt_fatal(str, __FILE__, __LINE__); |
|
||||
|
|
||||
#if defined(ENABLE_ASSERTIONS) || defined(ENABLE_HARDENING) |
|
||||
#ifdef __GNUC__ |
|
||||
__attribute__((noreturn)) |
|
||||
#endif |
|
||||
void celt_fatal(const char *str, const char *file, int line); |
|
||||
|
|
||||
#if defined(CELT_C) && !defined(OVERRIDE_celt_fatal) |
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#ifdef __GNUC__ |
|
||||
__attribute__((noreturn)) |
|
||||
#endif |
|
||||
void celt_fatal(const char *str, const char *file, int line) |
|
||||
{ |
|
||||
fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); |
|
||||
#if defined(_MSC_VER) |
|
||||
_set_abort_behavior( 0, _WRITE_ABORT_MSG); |
|
||||
#endif |
|
||||
abort(); |
|
||||
} |
|
||||
#endif |
|
||||
|
|
||||
#define celt_assert(cond) {if (!(cond)) {CELT_FATAL("assertion failed: " #cond);}} |
|
||||
#define celt_assert2(cond, message) {if (!(cond)) {CELT_FATAL("assertion failed: " #cond "\n" message);}} |
|
||||
#define MUST_SUCCEED(call) celt_assert((call) == OPUS_OK) |
|
||||
#else |
|
||||
#define celt_assert(cond) |
|
||||
#define celt_assert2(cond, message) |
|
||||
#define MUST_SUCCEED(call) do {if((call) != OPUS_OK) {RESTORE_STACK; return OPUS_INTERNAL_ERROR;} } while (0) |
|
||||
#endif |
|
||||
|
|
||||
#if defined(ENABLE_ASSERTIONS) |
|
||||
#define celt_sig_assert(cond) {if (!(cond)) {CELT_FATAL("signal assertion failed: " #cond);}} |
|
||||
#else |
|
||||
#define celt_sig_assert(cond) |
|
||||
#endif |
|
||||
|
|
||||
#define IMUL32(a,b) ((a)*(b)) |
|
||||
|
|
||||
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */ |
|
||||
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ |
|
||||
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */ |
|
||||
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ |
|
||||
#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */ |
|
||||
#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */ |
|
||||
#define UADD32(a,b) ((a)+(b)) |
|
||||
#define USUB32(a,b) ((a)-(b)) |
|
||||
|
|
||||
/* Set this if opus_int64 is a native type of the CPU. */ |
|
||||
/* Assume that all LP64 architectures have fast 64-bit types; also x86_64
|
|
||||
(which can be ILP32 for x32) and Win64 (which is LLP64). */ |
|
||||
#if defined(__x86_64__) || defined(__LP64__) || defined(_WIN64) |
|
||||
#define OPUS_FAST_INT64 1 |
|
||||
#else |
|
||||
#define OPUS_FAST_INT64 0 |
|
||||
#endif |
|
||||
|
|
||||
#define PRINT_MIPS(file) |
|
||||
|
|
||||
#ifdef FIXED_POINT |
|
||||
|
|
||||
typedef opus_int16 opus_val16; |
|
||||
typedef opus_int32 opus_val32; |
|
||||
typedef opus_int64 opus_val64; |
|
||||
|
|
||||
typedef opus_val32 celt_sig; |
|
||||
typedef opus_val16 celt_norm; |
|
||||
typedef opus_val32 celt_ener; |
|
||||
|
|
||||
#define celt_isnan(x) 0 |
|
||||
|
|
||||
#define Q15ONE 32767 |
|
||||
|
|
||||
#define SIG_SHIFT 12 |
|
||||
/* Safe saturation value for 32-bit signals. Should be less than
|
|
||||
2^31*(1-0.85) to avoid blowing up on DC at deemphasis.*/ |
|
||||
#define SIG_SAT (300000000) |
|
||||
|
|
||||
#define NORM_SCALING 16384 |
|
||||
|
|
||||
#define DB_SHIFT 10 |
|
||||
|
|
||||
#define EPSILON 1 |
|
||||
#define VERY_SMALL 0 |
|
||||
#define VERY_LARGE16 ((opus_val16)32767) |
|
||||
#define Q15_ONE ((opus_val16)32767) |
|
||||
|
|
||||
#define SCALEIN(a) (a) |
|
||||
#define SCALEOUT(a) (a) |
|
||||
|
|
||||
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) |
|
||||
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) |
|
||||
|
|
||||
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) { |
|
||||
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x; |
|
||||
} |
|
||||
|
|
||||
#ifdef FIXED_DEBUG |
|
||||
#include "fixed_debug.h" |
|
||||
#else |
|
||||
|
|
||||
#include "fixed_generic.h" |
|
||||
|
|
||||
#ifdef OPUS_ARM_PRESUME_AARCH64_NEON_INTR |
|
||||
#include "arm/fixed_arm64.h" |
|
||||
#elif defined (OPUS_ARM_INLINE_EDSP) |
|
||||
#include "arm/fixed_armv5e.h" |
|
||||
#elif defined (OPUS_ARM_INLINE_ASM) |
|
||||
#include "arm/fixed_armv4.h" |
|
||||
#elif defined (BFIN_ASM) |
|
||||
#include "fixed_bfin.h" |
|
||||
#elif defined (TI_C5X_ASM) |
|
||||
#include "fixed_c5x.h" |
|
||||
#elif defined (TI_C6X_ASM) |
|
||||
#include "fixed_c6x.h" |
|
||||
#endif |
|
||||
|
|
||||
#endif |
|
||||
|
|
||||
#else /* FIXED_POINT */ |
|
||||
|
|
||||
typedef float opus_val16; |
|
||||
typedef float opus_val32; |
|
||||
typedef float opus_val64; |
|
||||
|
|
||||
typedef float celt_sig; |
|
||||
typedef float celt_norm; |
|
||||
typedef float celt_ener; |
|
||||
|
|
||||
#ifdef FLOAT_APPROX |
|
||||
/* This code should reliably detect NaN/inf even when -ffast-math is used.
|
|
||||
Assumes IEEE 754 format. */ |
|
||||
static OPUS_INLINE int celt_isnan(float x) |
|
||||
{ |
|
||||
union {float f; opus_uint32 i;} in; |
|
||||
in.f = x; |
|
||||
return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0; |
|
||||
} |
|
||||
#else |
|
||||
#ifdef __FAST_MATH__ |
|
||||
#error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input |
|
||||
#endif |
|
||||
#define celt_isnan(x) ((x)!=(x)) |
|
||||
#endif |
|
||||
|
|
||||
#define Q15ONE 1.0f |
|
||||
|
|
||||
#define NORM_SCALING 1.f |
|
||||
|
|
||||
#define EPSILON 1e-15f |
|
||||
#define VERY_SMALL 1e-30f |
|
||||
#define VERY_LARGE16 1e15f |
|
||||
#define Q15_ONE ((opus_val16)1.f) |
|
||||
|
|
||||
/* This appears to be the same speed as C99's fabsf() but it's more portable. */ |
|
||||
#define ABS16(x) ((float)fabs(x)) |
|
||||
#define ABS32(x) ((float)fabs(x)) |
|
||||
|
|
||||
#define QCONST16(x,bits) (x) |
|
||||
#define QCONST32(x,bits) (x) |
|
||||
|
|
||||
#define NEG16(x) (-(x)) |
|
||||
#define NEG32(x) (-(x)) |
|
||||
#define NEG32_ovflw(x) (-(x)) |
|
||||
#define EXTRACT16(x) (x) |
|
||||
#define EXTEND32(x) (x) |
|
||||
#define SHR16(a,shift) (a) |
|
||||
#define SHL16(a,shift) (a) |
|
||||
#define SHR32(a,shift) (a) |
|
||||
#define SHL32(a,shift) (a) |
|
||||
#define PSHR32(a,shift) (a) |
|
||||
#define VSHR32(a,shift) (a) |
|
||||
|
|
||||
#define PSHR(a,shift) (a) |
|
||||
#define SHR(a,shift) (a) |
|
||||
#define SHL(a,shift) (a) |
|
||||
#define SATURATE(x,a) (x) |
|
||||
#define SATURATE16(x) (x) |
|
||||
|
|
||||
#define ROUND16(a,shift) (a) |
|
||||
#define SROUND16(a,shift) (a) |
|
||||
#define HALF16(x) (.5f*(x)) |
|
||||
#define HALF32(x) (.5f*(x)) |
|
||||
|
|
||||
#define ADD16(a,b) ((a)+(b)) |
|
||||
#define SUB16(a,b) ((a)-(b)) |
|
||||
#define ADD32(a,b) ((a)+(b)) |
|
||||
#define SUB32(a,b) ((a)-(b)) |
|
||||
#define ADD32_ovflw(a,b) ((a)+(b)) |
|
||||
#define SUB32_ovflw(a,b) ((a)-(b)) |
|
||||
#define MULT16_16_16(a,b) ((a)*(b)) |
|
||||
#define MULT16_16(a,b) ((opus_val32)(a)*(opus_val32)(b)) |
|
||||
#define MAC16_16(c,a,b) ((c)+(opus_val32)(a)*(opus_val32)(b)) |
|
||||
|
|
||||
#define MULT16_32_Q15(a,b) ((a)*(b)) |
|
||||
#define MULT16_32_Q16(a,b) ((a)*(b)) |
|
||||
|
|
||||
#define MULT32_32_Q31(a,b) ((a)*(b)) |
|
||||
|
|
||||
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) |
|
||||
#define MAC16_32_Q16(c,a,b) ((c)+(a)*(b)) |
|
||||
|
|
||||
#define MULT16_16_Q11_32(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_Q11(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_Q13(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_Q14(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_Q15(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_P15(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_P13(a,b) ((a)*(b)) |
|
||||
#define MULT16_16_P14(a,b) ((a)*(b)) |
|
||||
#define MULT16_32_P16(a,b) ((a)*(b)) |
|
||||
|
|
||||
#define DIV32_16(a,b) (((opus_val32)(a))/(opus_val16)(b)) |
|
||||
#define DIV32(a,b) (((opus_val32)(a))/(opus_val32)(b)) |
|
||||
|
|
||||
#define SCALEIN(a) ((a)*CELT_SIG_SCALE) |
|
||||
#define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE)) |
|
||||
|
|
||||
#define SIG2WORD16(x) (x) |
|
||||
|
|
||||
#endif /* !FIXED_POINT */ |
|
||||
|
|
||||
#ifndef GLOBAL_STACK_SIZE |
|
||||
#ifdef FIXED_POINT |
|
||||
#define GLOBAL_STACK_SIZE 120000 |
|
||||
#else |
|
||||
#define GLOBAL_STACK_SIZE 120000 |
|
||||
#endif |
|
||||
#endif |
|
||||
|
|
||||
#endif /* ARCH_H */ |
|
||||
@ -1,353 +0,0 @@ |
|||||
#!/usr/bin/perl |
|
||||
# Copyright (C) 2002-2013 Xiph.org Foundation |
|
||||
# |
|
||||
# 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. |
|
||||
|
|
||||
my $bigend; # little/big endian |
|
||||
my $nxstack; |
|
||||
my $apple = 0; |
|
||||
my $symprefix = ""; |
|
||||
|
|
||||
$nxstack = 0; |
|
||||
|
|
||||
eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}' |
|
||||
if $running_under_some_shell; |
|
||||
|
|
||||
while ($ARGV[0] =~ /^-/) { |
|
||||
$_ = shift; |
|
||||
last if /^--$/; |
|
||||
if (/^-n$/) { |
|
||||
$nflag++; |
|
||||
next; |
|
||||
} |
|
||||
if (/^--apple$/) { |
|
||||
$apple = 1; |
|
||||
$symprefix = "_"; |
|
||||
next; |
|
||||
} |
|
||||
die "I don't recognize this switch: $_\\n"; |
|
||||
} |
|
||||
$printit++ unless $nflag; |
|
||||
|
|
||||
$\ = "\n"; # automatically add newline on print |
|
||||
$n=0; |
|
||||
|
|
||||
$thumb = 0; # ARM mode by default, not Thumb. |
|
||||
@proc_stack = (); |
|
||||
|
|
||||
printf (" .syntax unified\n"); |
|
||||
|
|
||||
LINE: |
|
||||
while (<>) { |
|
||||
|
|
||||
# For ADRLs we need to add a new line after the substituted one. |
|
||||
$addPadding = 0; |
|
||||
|
|
||||
# First, we do not dare to touch *anything* inside double quotes, do we? |
|
||||
# Second, if you want a dollar character in the string, |
|
||||
# insert two of them -- that's how ARM C and assembler treat strings. |
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB[ \t]*\"/$1: .ascii \"/ && do { s/\$\$/\$/g; next }; |
|
||||
s/\bDCB\b[ \t]*\"/.ascii \"/ && do { s/\$\$/\$/g; next }; |
|
||||
s/^(\S+)\s+RN\s+(\S+)/$1 .req r$2/ && do { s/\$\$/\$/g; next }; |
|
||||
# If there's nothing on a line but a comment, don't try to apply any further |
|
||||
# substitutions (this is a cheap hack to avoid mucking up the license header) |
|
||||
s/^([ \t]*);/$1@/ && do { s/\$\$/\$/g; next }; |
|
||||
# If substituted -- leave immediately ! |
|
||||
|
|
||||
s/@/,:/; |
|
||||
s/;/@/; |
|
||||
while ( /@.*'/ ) { |
|
||||
s/(@.*)'/$1/g; |
|
||||
} |
|
||||
s/\{FALSE\}/0/g; |
|
||||
s/\{TRUE\}/1/g; |
|
||||
s/\{(\w\w\w\w+)\}/$1/g; |
|
||||
s/\bINCLUDE[ \t]*([^ \t\n]+)/.include \"$1\"/; |
|
||||
s/\bGET[ \t]*([^ \t\n]+)/.include \"${ my $x=$1; $x =~ s|\.s|-gnu.S|; \$x }\"/; |
|
||||
s/\bIMPORT\b/.extern/; |
|
||||
s/\bEXPORT\b\s*/.global $symprefix/; |
|
||||
s/^(\s+)\[/$1IF/; |
|
||||
s/^(\s+)\|/$1ELSE/; |
|
||||
s/^(\s+)\]/$1ENDIF/; |
|
||||
s/IF *:DEF:/ .ifdef/; |
|
||||
s/IF *:LNOT: *:DEF:/ .ifndef/; |
|
||||
s/ELSE/ .else/; |
|
||||
s/ENDIF/ .endif/; |
|
||||
|
|
||||
if( /\bIF\b/ ) { |
|
||||
s/\bIF\b/ .if/; |
|
||||
s/=/==/; |
|
||||
} |
|
||||
if ( $n == 2) { |
|
||||
s/\$/\\/g; |
|
||||
} |
|
||||
if ($n == 1) { |
|
||||
s/\$//g; |
|
||||
s/label//g; |
|
||||
$n = 2; |
|
||||
} |
|
||||
if ( /MACRO/ ) { |
|
||||
s/MACRO *\n/.macro/; |
|
||||
$n=1; |
|
||||
} |
|
||||
if ( /\bMEND\b/ ) { |
|
||||
s/\bMEND\b/.endm/; |
|
||||
$n=0; |
|
||||
} |
|
||||
|
|
||||
# ".rdata" doesn't work in 'as' version 2.13.2, as it is ".rodata" there. |
|
||||
# |
|
||||
if ( /\bAREA\b/ ) { |
|
||||
my $align; |
|
||||
$align = "2"; |
|
||||
if ( /ALIGN=(\d+)/ ) { |
|
||||
$align = $1; |
|
||||
} |
|
||||
if ( /CODE/ ) { |
|
||||
$nxstack = 1; |
|
||||
} |
|
||||
s/^(.+)CODE(.+)READONLY(.*)/ .text/; |
|
||||
s/^(.+)DATA(.+)READONLY(.*)/ .section .rdata/; |
|
||||
s/^(.+)\|\|\.data\|\|(.+)/ .data/; |
|
||||
s/^(.+)\|\|\.bss\|\|(.+)/ .bss/; |
|
||||
s/$/; .p2align $align/; |
|
||||
# Enable NEON instructions but don't produce a binary that requires |
|
||||
# ARMv7. RVCT does not have equivalent directives, so we just do this |
|
||||
# for all CODE areas. |
|
||||
if ( /.text/ ) { |
|
||||
# Separating .arch, .fpu, etc., by semicolons does not work (gas |
|
||||
# thinks the semicolon is part of the arch name, even when there's |
|
||||
# whitespace separating them). Sadly this means our line numbers |
|
||||
# won't match the original source file (we could use the .line |
|
||||
# directive, which is documented to be obsolete, but then gdb will |
|
||||
# show the wrong line in the translated source file). |
|
||||
s/$/; .arch armv7-a\n .fpu neon\n .object_arch armv4t/ unless ($apple); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
s/\|\|\.constdata\$(\d+)\|\|/.L_CONST$1/; # ||.constdata$3|| |
|
||||
s/\|\|\.bss\$(\d+)\|\|/.L_BSS$1/; # ||.bss$2|| |
|
||||
s/\|\|\.data\$(\d+)\|\|/.L_DATA$1/; # ||.data$2|| |
|
||||
s/\|\|([a-zA-Z0-9_]+)\@([a-zA-Z0-9_]+)\|\|/@ $&/; |
|
||||
s/^(\s+)\%(\s)/ .space $1/; |
|
||||
|
|
||||
s/\|(.+)\.(\d+)\|/\.$1_$2/; # |L80.123| -> .L80_123 |
|
||||
s/\bCODE32\b/.code 32/ && do {$thumb = 0}; |
|
||||
s/\bCODE16\b/.code 16/ && do {$thumb = 1}; |
|
||||
if (/\bPROC\b/) |
|
||||
{ |
|
||||
my $prefix; |
|
||||
my $proc; |
|
||||
/^([A-Za-z_\.]\w+)\b/; |
|
||||
$proc = $1; |
|
||||
$prefix = ""; |
|
||||
if ($proc) |
|
||||
{ |
|
||||
$prefix = $prefix.sprintf("\t.type\t%s, %%function", $proc) unless ($apple); |
|
||||
# Make sure we $prefix isn't empty here (for the $apple case). |
|
||||
# We handle mangling the label here, make sure it doesn't match |
|
||||
# the label handling below (if $prefix would be empty). |
|
||||
$prefix = $prefix."; "; |
|
||||
push(@proc_stack, $proc); |
|
||||
s/^[A-Za-z_\.]\w+/$symprefix$&:/; |
|
||||
} |
|
||||
$prefix = $prefix."\t.thumb_func; " if ($thumb); |
|
||||
s/\bPROC\b/@ $&/; |
|
||||
$_ = $prefix.$_; |
|
||||
} |
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)ASX\b/$1$2ADDSUBX/; |
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)SAX\b/$1$2SUBADDX/; |
|
||||
if (/\bENDP\b/) |
|
||||
{ |
|
||||
my $proc; |
|
||||
s/\bENDP\b/@ $&/; |
|
||||
$proc = pop(@proc_stack); |
|
||||
$_ = "\t.size $proc, .-$proc".$_ if ($proc && !$apple); |
|
||||
} |
|
||||
s/\bSUBT\b/@ $&/; |
|
||||
s/\bDATA\b/@ $&/; # DATA directive is deprecated -- Asm guide, p.7-25 |
|
||||
s/\bKEEP\b/@ $&/; |
|
||||
s/\bEXPORTAS\b/@ $&/; |
|
||||
s/\|\|(.)+\bEQU\b/@ $&/; |
|
||||
s/\|\|([\w\$]+)\|\|/$1/; |
|
||||
s/\bENTRY\b/@ $&/; |
|
||||
s/\bASSERT\b/@ $&/; |
|
||||
s/\bGBLL\b/@ $&/; |
|
||||
s/\bGBLA\b/@ $&/; |
|
||||
s/^\W+OPT\b/@ $&/; |
|
||||
s/:OR:/|/g; |
|
||||
s/:SHL:/<</g; |
|
||||
s/:SHR:/>>/g; |
|
||||
s/:AND:/&/g; |
|
||||
s/:LAND:/&&/g; |
|
||||
s/CPSR/cpsr/; |
|
||||
s/SPSR/spsr/; |
|
||||
s/ALIGN$/.balign 4/; |
|
||||
s/ALIGN\s+([0-9x]+)$/.balign $1/; |
|
||||
s/psr_cxsf/psr_all/; |
|
||||
s/LTORG/.ltorg/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+EQU/ .set $1,/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETL/ .set $1,/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETA/ .set $1,/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+\*/ .set $1,/; |
|
||||
|
|
||||
# {PC} + 0xdeadfeed --> . + 0xdeadfeed |
|
||||
s/\{PC\} \+/ \. +/; |
|
||||
|
|
||||
# Single hex constant on the line ! |
|
||||
# |
|
||||
# >>> NOTE <<< |
|
||||
# Double-precision floats in gcc are always mixed-endian, which means |
|
||||
# bytes in two words are little-endian, but words are big-endian. |
|
||||
# So, 0x0000deadfeed0000 would be stored as 0x0000dead at low address |
|
||||
# and 0xfeed0000 at high address. |
|
||||
# |
|
||||
s/\bDCFD\b[ \t]+0x([a-fA-F0-9]{8})([a-fA-F0-9]{8})/.long 0x$1, 0x$2/; |
|
||||
# Only decimal constants on the line, no hex ! |
|
||||
s/\bDCFD\b[ \t]+([0-9\.\-]+)/.double $1/; |
|
||||
|
|
||||
# Single hex constant on the line ! |
|
||||
# s/\bDCFS\b[ \t]+0x([a-f0-9]{8})([a-f0-9]{8})/.long 0x$1, 0x$2/; |
|
||||
# Only decimal constants on the line, no hex ! |
|
||||
# s/\bDCFS\b[ \t]+([0-9\.\-]+)/.double $1/; |
|
||||
s/\bDCFS[ \t]+0x/.word 0x/; |
|
||||
s/\bDCFS\b/.float/; |
|
||||
|
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCD/$1 .word/; |
|
||||
s/\bDCD\b/.word/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCW/$1 .short/; |
|
||||
s/\bDCW\b/.short/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB/$1 .byte/; |
|
||||
s/\bDCB\b/.byte/; |
|
||||
s/^([A-Za-z_]\w*)[ \t]+\%/.comm $1,/; |
|
||||
s/^[A-Za-z_\.]\w+/$&:/; |
|
||||
s/^(\d+)/$1:/; |
|
||||
s/\%(\d+)/$1b_or_f/; |
|
||||
s/\%[Bb](\d+)/$1b/; |
|
||||
s/\%[Ff](\d+)/$1f/; |
|
||||
s/\%[Ff][Tt](\d+)/$1f/; |
|
||||
s/&([\dA-Fa-f]+)/0x$1/; |
|
||||
if ( /\b2_[01]+\b/ ) { |
|
||||
s/\b2_([01]+)\b/conv$1&&&&/g; |
|
||||
while ( /[01][01][01][01]&&&&/ ) { |
|
||||
s/0000&&&&/&&&&0/g; |
|
||||
s/0001&&&&/&&&&1/g; |
|
||||
s/0010&&&&/&&&&2/g; |
|
||||
s/0011&&&&/&&&&3/g; |
|
||||
s/0100&&&&/&&&&4/g; |
|
||||
s/0101&&&&/&&&&5/g; |
|
||||
s/0110&&&&/&&&&6/g; |
|
||||
s/0111&&&&/&&&&7/g; |
|
||||
s/1000&&&&/&&&&8/g; |
|
||||
s/1001&&&&/&&&&9/g; |
|
||||
s/1010&&&&/&&&&A/g; |
|
||||
s/1011&&&&/&&&&B/g; |
|
||||
s/1100&&&&/&&&&C/g; |
|
||||
s/1101&&&&/&&&&D/g; |
|
||||
s/1110&&&&/&&&&E/g; |
|
||||
s/1111&&&&/&&&&F/g; |
|
||||
} |
|
||||
s/000&&&&/&&&&0/g; |
|
||||
s/001&&&&/&&&&1/g; |
|
||||
s/010&&&&/&&&&2/g; |
|
||||
s/011&&&&/&&&&3/g; |
|
||||
s/100&&&&/&&&&4/g; |
|
||||
s/101&&&&/&&&&5/g; |
|
||||
s/110&&&&/&&&&6/g; |
|
||||
s/111&&&&/&&&&7/g; |
|
||||
s/00&&&&/&&&&0/g; |
|
||||
s/01&&&&/&&&&1/g; |
|
||||
s/10&&&&/&&&&2/g; |
|
||||
s/11&&&&/&&&&3/g; |
|
||||
s/0&&&&/&&&&0/g; |
|
||||
s/1&&&&/&&&&1/g; |
|
||||
s/conv&&&&/0x/g; |
|
||||
} |
|
||||
|
|
||||
if ( /commandline/) |
|
||||
{ |
|
||||
if( /-bigend/) |
|
||||
{ |
|
||||
$bigend=1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if ( /\bDCDU\b/ ) |
|
||||
{ |
|
||||
my $cmd=$_; |
|
||||
my $value; |
|
||||
my $prefix; |
|
||||
my $w1; |
|
||||
my $w2; |
|
||||
my $w3; |
|
||||
my $w4; |
|
||||
|
|
||||
s/\s+DCDU\b/@ $&/; |
|
||||
|
|
||||
$cmd =~ /\bDCDU\b\s+0x(\d+)/; |
|
||||
$value = $1; |
|
||||
$value =~ /(\w\w)(\w\w)(\w\w)(\w\w)/; |
|
||||
$w1 = $1; |
|
||||
$w2 = $2; |
|
||||
$w3 = $3; |
|
||||
$w4 = $4; |
|
||||
|
|
||||
if( $bigend ne "") |
|
||||
{ |
|
||||
# big endian |
|
||||
$prefix = "\t.byte\t0x".$w1.";". |
|
||||
"\t.byte\t0x".$w2.";". |
|
||||
"\t.byte\t0x".$w3.";". |
|
||||
"\t.byte\t0x".$w4."; "; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
# little endian |
|
||||
$prefix = "\t.byte\t0x".$w4.";". |
|
||||
"\t.byte\t0x".$w3.";". |
|
||||
"\t.byte\t0x".$w2.";". |
|
||||
"\t.byte\t0x".$w1."; "; |
|
||||
} |
|
||||
$_=$prefix.$_; |
|
||||
} |
|
||||
|
|
||||
if ( /\badrl\b/i ) |
|
||||
{ |
|
||||
s/\badrl\s+(\w+)\s*,\s*(\w+)/ldr $1,=$2/i; |
|
||||
$addPadding = 1; |
|
||||
} |
|
||||
s/\bEND\b/@ END/; |
|
||||
} continue { |
|
||||
printf ("%s", $_) if $printit; |
|
||||
if ($addPadding != 0) |
|
||||
{ |
|
||||
printf (" mov r0,r0\n"); |
|
||||
$addPadding = 0; |
|
||||
} |
|
||||
} |
|
||||
#If we had a code section, mark that this object doesn't need an executable |
|
||||
# stack. |
|
||||
if ($nxstack && !$apple) { |
|
||||
printf (" .section\t.note.GNU-stack,\"\",\%\%progbits\n"); |
|
||||
} |
|
||||
@ -1,171 +0,0 @@ |
|||||
/* Copyright (c) 2010 Xiph.Org Foundation
|
|
||||
* Copyright (c) 2013 Parrot */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifdef HAVE_CONFIG_H |
|
||||
#include "config.h" |
|
||||
#endif |
|
||||
|
|
||||
#include "pitch.h" |
|
||||
#include "kiss_fft.h" |
|
||||
#include "mdct.h" |
|
||||
|
|
||||
#if defined(OPUS_HAVE_RTCD) |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR) |
|
||||
opus_val32 (*const CELT_INNER_PROD_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *x, const opus_val16 *y, int N) = { |
|
||||
celt_inner_prod_c, /* ARMv4 */ |
|
||||
celt_inner_prod_c, /* EDSP */ |
|
||||
celt_inner_prod_c, /* Media */ |
|
||||
celt_inner_prod_neon,/* NEON */ |
|
||||
celt_inner_prod_neon /* DOTPROD */ |
|
||||
}; |
|
||||
|
|
||||
void (*const DUAL_INNER_PROD_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, |
|
||||
int N, opus_val32 *xy1, opus_val32 *xy2) = { |
|
||||
dual_inner_prod_c, /* ARMv4 */ |
|
||||
dual_inner_prod_c, /* EDSP */ |
|
||||
dual_inner_prod_c, /* Media */ |
|
||||
dual_inner_prod_neon,/* NEON */ |
|
||||
dual_inner_prod_neon /* DOTPROD */ |
|
||||
}; |
|
||||
# endif |
|
||||
|
|
||||
# if defined(FIXED_POINT) |
|
||||
# if ((defined(OPUS_ARM_MAY_HAVE_NEON) && !defined(OPUS_ARM_PRESUME_NEON)) || \ |
|
||||
(defined(OPUS_ARM_MAY_HAVE_MEDIA) && !defined(OPUS_ARM_PRESUME_MEDIA)) || \ |
|
||||
(defined(OPUS_ARM_MAY_HAVE_EDSP) && !defined(OPUS_ARM_PRESUME_EDSP))) |
|
||||
opus_val32 (*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, |
|
||||
const opus_val16 *, opus_val32 *, int, int, int) = { |
|
||||
celt_pitch_xcorr_c, /* ARMv4 */ |
|
||||
MAY_HAVE_EDSP(celt_pitch_xcorr), /* EDSP */ |
|
||||
MAY_HAVE_MEDIA(celt_pitch_xcorr), /* Media */ |
|
||||
MAY_HAVE_NEON(celt_pitch_xcorr), /* NEON */ |
|
||||
MAY_HAVE_NEON(celt_pitch_xcorr) /* DOTPROD */ |
|
||||
}; |
|
||||
|
|
||||
# endif |
|
||||
# else /* !FIXED_POINT */ |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR) |
|
||||
void (*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, |
|
||||
const opus_val16 *, opus_val32 *, int, int, int) = { |
|
||||
celt_pitch_xcorr_c, /* ARMv4 */ |
|
||||
celt_pitch_xcorr_c, /* EDSP */ |
|
||||
celt_pitch_xcorr_c, /* Media */ |
|
||||
celt_pitch_xcorr_float_neon, /* Neon */ |
|
||||
celt_pitch_xcorr_float_neon /* DOTPROD */ |
|
||||
}; |
|
||||
# endif |
|
||||
# endif /* FIXED_POINT */ |
|
||||
|
|
||||
#if defined(FIXED_POINT) && defined(OPUS_HAVE_RTCD) && \ |
|
||||
defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR) |
|
||||
|
|
||||
void (*const XCORR_KERNEL_IMPL[OPUS_ARCHMASK + 1])( |
|
||||
const opus_val16 *x, |
|
||||
const opus_val16 *y, |
|
||||
opus_val32 sum[4], |
|
||||
int len |
|
||||
) = { |
|
||||
xcorr_kernel_c, /* ARMv4 */ |
|
||||
xcorr_kernel_c, /* EDSP */ |
|
||||
xcorr_kernel_c, /* Media */ |
|
||||
xcorr_kernel_neon_fixed, /* Neon */ |
|
||||
xcorr_kernel_neon_fixed /* DOTPROD */ |
|
||||
}; |
|
||||
|
|
||||
#endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
# if defined(HAVE_ARM_NE10) |
|
||||
# if defined(CUSTOM_MODES) |
|
||||
int (*const OPUS_FFT_ALLOC_ARCH_IMPL[OPUS_ARCHMASK+1])(kiss_fft_state *st) = { |
|
||||
opus_fft_alloc_arch_c, /* ARMv4 */ |
|
||||
opus_fft_alloc_arch_c, /* EDSP */ |
|
||||
opus_fft_alloc_arch_c, /* Media */ |
|
||||
opus_fft_alloc_arm_neon, /* Neon with NE10 library support */ |
|
||||
opus_fft_alloc_arm_neon /* DOTPROD with NE10 library support */ |
|
||||
}; |
|
||||
|
|
||||
void (*const OPUS_FFT_FREE_ARCH_IMPL[OPUS_ARCHMASK+1])(kiss_fft_state *st) = { |
|
||||
opus_fft_free_arch_c, /* ARMv4 */ |
|
||||
opus_fft_free_arch_c, /* EDSP */ |
|
||||
opus_fft_free_arch_c, /* Media */ |
|
||||
opus_fft_free_arm_neon, /* Neon with NE10 */ |
|
||||
opus_fft_free_arm_neon /* DOTPROD with NE10 */ |
|
||||
}; |
|
||||
# endif /* CUSTOM_MODES */ |
|
||||
|
|
||||
void (*const OPUS_FFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout) = { |
|
||||
opus_fft_c, /* ARMv4 */ |
|
||||
opus_fft_c, /* EDSP */ |
|
||||
opus_fft_c, /* Media */ |
|
||||
opus_fft_neon, /* Neon with NE10 */ |
|
||||
opus_fft_neon /* DOTPROD with NE10 */ |
|
||||
}; |
|
||||
|
|
||||
void (*const OPUS_IFFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout) = { |
|
||||
opus_ifft_c, /* ARMv4 */ |
|
||||
opus_ifft_c, /* EDSP */ |
|
||||
opus_ifft_c, /* Media */ |
|
||||
opus_ifft_neon, /* Neon with NE10 */ |
|
||||
opus_ifft_neon /* DOTPROD with NE10 */ |
|
||||
}; |
|
||||
|
|
||||
void (*const CLT_MDCT_FORWARD_IMPL[OPUS_ARCHMASK+1])(const mdct_lookup *l, |
|
||||
kiss_fft_scalar *in, |
|
||||
kiss_fft_scalar * OPUS_RESTRICT out, |
|
||||
const opus_val16 *window, |
|
||||
int overlap, int shift, |
|
||||
int stride, int arch) = { |
|
||||
clt_mdct_forward_c, /* ARMv4 */ |
|
||||
clt_mdct_forward_c, /* EDSP */ |
|
||||
clt_mdct_forward_c, /* Media */ |
|
||||
clt_mdct_forward_neon, /* Neon with NE10 */ |
|
||||
clt_mdct_forward_neon /* DOTPROD with NE10 */ |
|
||||
}; |
|
||||
|
|
||||
void (*const CLT_MDCT_BACKWARD_IMPL[OPUS_ARCHMASK+1])(const mdct_lookup *l, |
|
||||
kiss_fft_scalar *in, |
|
||||
kiss_fft_scalar * OPUS_RESTRICT out, |
|
||||
const opus_val16 *window, |
|
||||
int overlap, int shift, |
|
||||
int stride, int arch) = { |
|
||||
clt_mdct_backward_c, /* ARMv4 */ |
|
||||
clt_mdct_backward_c, /* EDSP */ |
|
||||
clt_mdct_backward_c, /* Media */ |
|
||||
clt_mdct_backward_neon, /* Neon with NE10 */ |
|
||||
clt_mdct_backward_neon /* DOTPROD with NE10 */ |
|
||||
}; |
|
||||
|
|
||||
# endif /* HAVE_ARM_NE10 */ |
|
||||
# endif /* OPUS_ARM_MAY_HAVE_NEON_INTR */ |
|
||||
|
|
||||
#endif /* OPUS_HAVE_RTCD */ |
|
||||
@ -1,291 +0,0 @@ |
|||||
/* Copyright (c) 2010 Xiph.Org Foundation
|
|
||||
* Copyright (c) 2013 Parrot */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
/* Original code from libtheora modified to suit to Opus */ |
|
||||
|
|
||||
#ifdef HAVE_CONFIG_H |
|
||||
#include "config.h" |
|
||||
#endif |
|
||||
|
|
||||
#ifdef OPUS_HAVE_RTCD |
|
||||
|
|
||||
#include "armcpu.h" |
|
||||
#include "cpu_support.h" |
|
||||
#include "os_support.h" |
|
||||
#include "opus_types.h" |
|
||||
#include "arch.h" |
|
||||
|
|
||||
#define OPUS_CPU_ARM_V4_FLAG (1<<OPUS_ARCH_ARM_V4) |
|
||||
#define OPUS_CPU_ARM_EDSP_FLAG (1<<OPUS_ARCH_ARM_EDSP) |
|
||||
#define OPUS_CPU_ARM_MEDIA_FLAG (1<<OPUS_ARCH_ARM_MEDIA) |
|
||||
#define OPUS_CPU_ARM_NEON_FLAG (1<<OPUS_ARCH_ARM_NEON) |
|
||||
#define OPUS_CPU_ARM_DOTPROD_FLAG (1<<OPUS_ARCH_ARM_DOTPROD) |
|
||||
|
|
||||
#if defined(_MSC_VER) |
|
||||
/*For GetExceptionCode() and EXCEPTION_ILLEGAL_INSTRUCTION.*/ |
|
||||
# define WIN32_LEAN_AND_MEAN |
|
||||
# define WIN32_EXTRA_LEAN |
|
||||
# include <windows.h> |
|
||||
|
|
||||
static OPUS_INLINE opus_uint32 opus_cpu_capabilities(void){ |
|
||||
opus_uint32 flags; |
|
||||
flags=0; |
|
||||
/* MSVC has no OPUS_INLINE __asm support for ARM, but it does let you __emit
|
|
||||
* instructions via their assembled hex code. |
|
||||
* All of these instructions should be essentially nops. */ |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
__try{ |
|
||||
/*PLD [r13]*/ |
|
||||
__emit(0xF5DDF000); |
|
||||
flags|=OPUS_CPU_ARM_EDSP_FLAG; |
|
||||
} |
|
||||
__except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ |
|
||||
/*Ignore exception.*/ |
|
||||
} |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
__try{ |
|
||||
/*SHADD8 r3,r3,r3*/ |
|
||||
__emit(0xE6333F93); |
|
||||
flags|=OPUS_CPU_ARM_MEDIA_FLAG; |
|
||||
} |
|
||||
__except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ |
|
||||
/*Ignore exception.*/ |
|
||||
} |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
__try{ |
|
||||
/*VORR q0,q0,q0*/ |
|
||||
__emit(0xF2200150); |
|
||||
flags|=OPUS_CPU_ARM_NEON_FLAG; |
|
||||
} |
|
||||
__except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ |
|
||||
/*Ignore exception.*/ |
|
||||
} |
|
||||
# endif |
|
||||
# endif |
|
||||
# endif |
|
||||
return flags; |
|
||||
} |
|
||||
|
|
||||
#elif defined(__linux__) |
|
||||
/* Linux based */ |
|
||||
#include <stdio.h> |
|
||||
|
|
||||
static opus_uint32 opus_cpu_capabilities(void) |
|
||||
{ |
|
||||
opus_uint32 flags = 0; |
|
||||
FILE *cpuinfo; |
|
||||
|
|
||||
/* Reading /proc/self/auxv would be easier, but that doesn't work reliably on
|
|
||||
* Android */ |
|
||||
cpuinfo = fopen("/proc/cpuinfo", "r"); |
|
||||
|
|
||||
if(cpuinfo != NULL) |
|
||||
{ |
|
||||
/* 512 should be enough for anybody (it's even enough for all the flags that
|
|
||||
* x86 has accumulated... so far). */ |
|
||||
char buf[512]; |
|
||||
|
|
||||
while(fgets(buf, 512, cpuinfo) != NULL) |
|
||||
{ |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
/* Search for edsp and neon flag */ |
|
||||
if(memcmp(buf, "Features", 8) == 0) |
|
||||
{ |
|
||||
char *p; |
|
||||
p = strstr(buf, " edsp"); |
|
||||
if(p != NULL && (p[5] == ' ' || p[5] == '\n')) |
|
||||
flags |= OPUS_CPU_ARM_EDSP_FLAG; |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
p = strstr(buf, " neon"); |
|
||||
if(p != NULL && (p[5] == ' ' || p[5] == '\n')) |
|
||||
flags |= OPUS_CPU_ARM_NEON_FLAG; |
|
||||
p = strstr(buf, " asimd"); |
|
||||
if(p != NULL && (p[6] == ' ' || p[6] == '\n')) |
|
||||
flags |= OPUS_CPU_ARM_NEON_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_EDSP_FLAG; |
|
||||
# endif |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_DOTPROD) |
|
||||
p = strstr(buf, " asimddp"); |
|
||||
if(p != NULL && (p[8] == ' ' || p[8] == '\n')) |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
# endif |
|
||||
} |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
/* Search for media capabilities (>= ARMv6) */ |
|
||||
if(memcmp(buf, "CPU architecture:", 17) == 0) |
|
||||
{ |
|
||||
int version; |
|
||||
version = atoi(buf+17); |
|
||||
|
|
||||
if(version >= 6) |
|
||||
flags |= OPUS_CPU_ARM_MEDIA_FLAG; |
|
||||
} |
|
||||
# endif |
|
||||
} |
|
||||
|
|
||||
#if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) |
|
||||
flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; |
|
||||
# if defined(OPUS_ARM_PRESUME_DOTPROD) |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
# endif |
|
||||
#endif |
|
||||
|
|
||||
fclose(cpuinfo); |
|
||||
} |
|
||||
return flags; |
|
||||
} |
|
||||
|
|
||||
#elif defined(__APPLE__) |
|
||||
#include <sys/types.h> |
|
||||
#include <sys/sysctl.h> |
|
||||
|
|
||||
static opus_uint32 opus_cpu_capabilities(void) |
|
||||
{ |
|
||||
opus_uint32 flags = 0; |
|
||||
|
|
||||
#if defined(OPUS_ARM_MAY_HAVE_DOTPROD) |
|
||||
size_t size = sizeof(uint32_t); |
|
||||
uint32_t value = 0; |
|
||||
if (!sysctlbyname("hw.optional.arm.FEAT_DotProd", &value, &size, NULL, 0) && value) |
|
||||
{ |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
} |
|
||||
#endif |
|
||||
|
|
||||
#if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) |
|
||||
flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; |
|
||||
# if defined(OPUS_ARM_PRESUME_DOTPROD) |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
# endif |
|
||||
#endif |
|
||||
return flags; |
|
||||
} |
|
||||
|
|
||||
#elif defined(__FreeBSD__) |
|
||||
#include <sys/auxv.h> |
|
||||
|
|
||||
static opus_uint32 opus_cpu_capabilities(void) |
|
||||
{ |
|
||||
long hwcap = 0; |
|
||||
opus_uint32 flags = 0; |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
/* FreeBSD requires armv6+, which always supports media instructions */ |
|
||||
flags |= OPUS_CPU_ARM_MEDIA_FLAG; |
|
||||
# endif |
|
||||
|
|
||||
elf_aux_info(AT_HWCAP, &hwcap, sizeof hwcap); |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_MEDIA) \ |
|
||||
|| defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
# ifdef HWCAP_EDSP |
|
||||
if (hwcap & HWCAP_EDSP) |
|
||||
flags |= OPUS_CPU_ARM_EDSP_FLAG; |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|
||||
# ifdef HWCAP_NEON |
|
||||
if (hwcap & HWCAP_NEON) |
|
||||
flags |= OPUS_CPU_ARM_NEON_FLAG; |
|
||||
# elif defined(HWCAP_ASIMD) |
|
||||
if (hwcap & HWCAP_ASIMD) |
|
||||
flags |= OPUS_CPU_ARM_NEON_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_EDSP_FLAG; |
|
||||
# endif |
|
||||
# endif |
|
||||
# if defined(OPUS_ARM_MAY_HAVE_DOTPROD) && defined(HWCAP_ASIMDDP) |
|
||||
if (hwcap & HWCAP_ASIMDDP) |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
# endif |
|
||||
# endif |
|
||||
|
|
||||
#if defined(OPUS_ARM_PRESUME_AARCH64_NEON_INTR) |
|
||||
flags |= OPUS_CPU_ARM_EDSP_FLAG | OPUS_CPU_ARM_MEDIA_FLAG | OPUS_CPU_ARM_NEON_FLAG; |
|
||||
# if defined(OPUS_ARM_PRESUME_DOTPROD) |
|
||||
flags |= OPUS_CPU_ARM_DOTPROD_FLAG; |
|
||||
# endif |
|
||||
#endif |
|
||||
|
|
||||
return (flags); |
|
||||
} |
|
||||
|
|
||||
#else |
|
||||
/* The feature registers which can tell us what the processor supports are
|
|
||||
* accessible in priveleged modes only, so we can't have a general user-space |
|
||||
* detection method like on x86.*/ |
|
||||
# error "Configured to use ARM asm but no CPU detection method available for " \ |
|
||||
"your platform. Reconfigure with --disable-rtcd (or send patches)." |
|
||||
#endif |
|
||||
|
|
||||
static int opus_select_arch_impl(void) |
|
||||
{ |
|
||||
opus_uint32 flags = opus_cpu_capabilities(); |
|
||||
int arch = 0; |
|
||||
|
|
||||
if(!(flags & OPUS_CPU_ARM_EDSP_FLAG)) { |
|
||||
/* Asserts ensure arch values are sequential */ |
|
||||
celt_assert(arch == OPUS_ARCH_ARM_V4); |
|
||||
return arch; |
|
||||
} |
|
||||
arch++; |
|
||||
|
|
||||
if(!(flags & OPUS_CPU_ARM_MEDIA_FLAG)) { |
|
||||
celt_assert(arch == OPUS_ARCH_ARM_EDSP); |
|
||||
return arch; |
|
||||
} |
|
||||
arch++; |
|
||||
|
|
||||
if(!(flags & OPUS_CPU_ARM_NEON_FLAG)) { |
|
||||
celt_assert(arch == OPUS_ARCH_ARM_MEDIA); |
|
||||
return arch; |
|
||||
} |
|
||||
arch++; |
|
||||
|
|
||||
if(!(flags & OPUS_CPU_ARM_DOTPROD_FLAG)) { |
|
||||
celt_assert(arch == OPUS_ARCH_ARM_NEON); |
|
||||
return arch; |
|
||||
} |
|
||||
arch++; |
|
||||
|
|
||||
celt_assert(arch == OPUS_ARCH_ARM_DOTPROD); |
|
||||
return arch; |
|
||||
} |
|
||||
|
|
||||
int opus_select_arch(void) { |
|
||||
int arch = opus_select_arch_impl(); |
|
||||
#ifdef FUZZING |
|
||||
arch = rand()%(arch+1); |
|
||||
#endif |
|
||||
return arch; |
|
||||
} |
|
||||
#endif |
|
||||
@ -1,90 +0,0 @@ |
|||||
/* Copyright (c) 2010 Xiph.Org Foundation
|
|
||||
* Copyright (c) 2013 Parrot */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#if !defined(ARMCPU_H) |
|
||||
# define ARMCPU_H |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_EDSP) |
|
||||
# define MAY_HAVE_EDSP(name) name ## _edsp |
|
||||
# else |
|
||||
# define MAY_HAVE_EDSP(name) name ## _c |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_MEDIA) |
|
||||
# define MAY_HAVE_MEDIA(name) name ## _media |
|
||||
# else |
|
||||
# define MAY_HAVE_MEDIA(name) MAY_HAVE_EDSP(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_NEON) |
|
||||
# define MAY_HAVE_NEON(name) name ## _neon |
|
||||
# else |
|
||||
# define MAY_HAVE_NEON(name) MAY_HAVE_MEDIA(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_MAY_HAVE_DOTPROD) |
|
||||
# define MAY_HAVE_DOTPROD(name) name ## _dotprod |
|
||||
# else |
|
||||
# define MAY_HAVE_DOTPROD(name) MAY_HAVE_NEON(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_PRESUME_EDSP) |
|
||||
# define PRESUME_EDSP(name) name ## _edsp |
|
||||
# else |
|
||||
# define PRESUME_EDSP(name) name ## _c |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_PRESUME_MEDIA) |
|
||||
# define PRESUME_MEDIA(name) name ## _media |
|
||||
# else |
|
||||
# define PRESUME_MEDIA(name) PRESUME_EDSP(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_PRESUME_NEON) |
|
||||
# define PRESUME_NEON(name) name ## _neon |
|
||||
# else |
|
||||
# define PRESUME_NEON(name) PRESUME_MEDIA(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_ARM_PRESUME_DOTPROD) |
|
||||
# define PRESUME_DOTPROD(name) name ## _dotprod |
|
||||
# else |
|
||||
# define PRESUME_DOTPROD(name) PRESUME_NEON(name) |
|
||||
# endif |
|
||||
|
|
||||
# if defined(OPUS_HAVE_RTCD) |
|
||||
int opus_select_arch(void); |
|
||||
|
|
||||
#define OPUS_ARCH_ARM_V4 (0) |
|
||||
#define OPUS_ARCH_ARM_EDSP (1) |
|
||||
#define OPUS_ARCH_ARM_MEDIA (2) |
|
||||
#define OPUS_ARCH_ARM_NEON (3) |
|
||||
#define OPUS_ARCH_ARM_DOTPROD (4) |
|
||||
|
|
||||
# endif |
|
||||
|
|
||||
#endif |
|
||||
@ -1,37 +0,0 @@ |
|||||
/* Copyright (C) 2013 Mozilla Corporation */ |
|
||||
/* |
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
; Set the following to 1 if we have EDSP instructions |
|
||||
; (LDRD/STRD, etc., ARMv5E and later). |
|
||||
OPUS_ARM_MAY_HAVE_EDSP * @OPUS_ARM_MAY_HAVE_EDSP@ |
|
||||
|
|
||||
; Set the following to 1 if we have ARMv6 media instructions. |
|
||||
OPUS_ARM_MAY_HAVE_MEDIA * @OPUS_ARM_MAY_HAVE_MEDIA@ |
|
||||
|
|
||||
; Set the following to 1 if we have NEON (some ARMv7) |
|
||||
OPUS_ARM_MAY_HAVE_NEON * @OPUS_ARM_MAY_HAVE_NEON@ |
|
||||
|
|
||||
END |
|
||||
@ -1,173 +0,0 @@ |
|||||
/* Copyright (c) 2015 Xiph.Org Foundation
|
|
||||
Written by Viswanath Puttagunta */ |
|
||||
/**
|
|
||||
@file celt_fft_ne10.c |
|
||||
@brief ARM Neon optimizations for fft using NE10 library |
|
||||
*/ |
|
||||
|
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef SKIP_CONFIG_H |
|
||||
#ifdef HAVE_CONFIG_H |
|
||||
#include "config.h" |
|
||||
#endif |
|
||||
#endif |
|
||||
|
|
||||
#include <NE10_dsp.h> |
|
||||
#include "os_support.h" |
|
||||
#include "kiss_fft.h" |
|
||||
#include "stack_alloc.h" |
|
||||
|
|
||||
#if !defined(FIXED_POINT) |
|
||||
# define NE10_FFT_ALLOC_C2C_TYPE_NEON ne10_fft_alloc_c2c_float32_neon |
|
||||
# define NE10_FFT_CFG_TYPE_T ne10_fft_cfg_float32_t |
|
||||
# define NE10_FFT_STATE_TYPE_T ne10_fft_state_float32_t |
|
||||
# define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_float32 |
|
||||
# define NE10_FFT_CPX_TYPE_T ne10_fft_cpx_float32_t |
|
||||
# define NE10_FFT_C2C_1D_TYPE_NEON ne10_fft_c2c_1d_float32_neon |
|
||||
#else |
|
||||
# define NE10_FFT_ALLOC_C2C_TYPE_NEON(nfft) ne10_fft_alloc_c2c_int32_neon(nfft) |
|
||||
# define NE10_FFT_CFG_TYPE_T ne10_fft_cfg_int32_t |
|
||||
# define NE10_FFT_STATE_TYPE_T ne10_fft_state_int32_t |
|
||||
# define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_int32 |
|
||||
# define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_int32 |
|
||||
# define NE10_FFT_CPX_TYPE_T ne10_fft_cpx_int32_t |
|
||||
# define NE10_FFT_C2C_1D_TYPE_NEON ne10_fft_c2c_1d_int32_neon |
|
||||
#endif |
|
||||
|
|
||||
#if defined(CUSTOM_MODES) |
|
||||
|
|
||||
/* nfft lengths in NE10 that support scaled fft */ |
|
||||
# define NE10_FFTSCALED_SUPPORT_MAX 4 |
|
||||
static const int ne10_fft_scaled_support[NE10_FFTSCALED_SUPPORT_MAX] = { |
|
||||
480, 240, 120, 60 |
|
||||
}; |
|
||||
|
|
||||
int opus_fft_alloc_arm_neon(kiss_fft_state *st) |
|
||||
{ |
|
||||
int i; |
|
||||
size_t memneeded = sizeof(struct arch_fft_state); |
|
||||
|
|
||||
st->arch_fft = (arch_fft_state *)opus_alloc(memneeded); |
|
||||
if (!st->arch_fft) |
|
||||
return -1; |
|
||||
|
|
||||
for (i = 0; i < NE10_FFTSCALED_SUPPORT_MAX; i++) { |
|
||||
if(st->nfft == ne10_fft_scaled_support[i]) |
|
||||
break; |
|
||||
} |
|
||||
if (i == NE10_FFTSCALED_SUPPORT_MAX) { |
|
||||
/* This nfft length (scaled fft) is not supported in NE10 */ |
|
||||
st->arch_fft->is_supported = 0; |
|
||||
st->arch_fft->priv = NULL; |
|
||||
} |
|
||||
else { |
|
||||
st->arch_fft->is_supported = 1; |
|
||||
st->arch_fft->priv = (void *)NE10_FFT_ALLOC_C2C_TYPE_NEON(st->nfft); |
|
||||
if (st->arch_fft->priv == NULL) { |
|
||||
return -1; |
|
||||
} |
|
||||
} |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
void opus_fft_free_arm_neon(kiss_fft_state *st) |
|
||||
{ |
|
||||
NE10_FFT_CFG_TYPE_T cfg; |
|
||||
|
|
||||
if (!st->arch_fft) |
|
||||
return; |
|
||||
|
|
||||
cfg = (NE10_FFT_CFG_TYPE_T)st->arch_fft->priv; |
|
||||
if (cfg) |
|
||||
NE10_FFT_DESTROY_C2C_TYPE(cfg); |
|
||||
opus_free(st->arch_fft); |
|
||||
} |
|
||||
#endif |
|
||||
|
|
||||
void opus_fft_neon(const kiss_fft_state *st, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout) |
|
||||
{ |
|
||||
NE10_FFT_STATE_TYPE_T state; |
|
||||
NE10_FFT_CFG_TYPE_T cfg = &state; |
|
||||
VARDECL(NE10_FFT_CPX_TYPE_T, buffer); |
|
||||
SAVE_STACK; |
|
||||
ALLOC(buffer, st->nfft, NE10_FFT_CPX_TYPE_T); |
|
||||
|
|
||||
if (!st->arch_fft->is_supported) { |
|
||||
/* This nfft length (scaled fft) not supported in NE10 */ |
|
||||
opus_fft_c(st, fin, fout); |
|
||||
} |
|
||||
else { |
|
||||
memcpy((void *)cfg, st->arch_fft->priv, sizeof(NE10_FFT_STATE_TYPE_T)); |
|
||||
state.buffer = (NE10_FFT_CPX_TYPE_T *)&buffer[0]; |
|
||||
#if !defined(FIXED_POINT) |
|
||||
state.is_forward_scaled = 1; |
|
||||
|
|
||||
NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, |
|
||||
(NE10_FFT_CPX_TYPE_T *)fin, |
|
||||
cfg, 0); |
|
||||
#else |
|
||||
NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, |
|
||||
(NE10_FFT_CPX_TYPE_T *)fin, |
|
||||
cfg, 0, 1); |
|
||||
#endif |
|
||||
} |
|
||||
RESTORE_STACK; |
|
||||
} |
|
||||
|
|
||||
void opus_ifft_neon(const kiss_fft_state *st, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout) |
|
||||
{ |
|
||||
NE10_FFT_STATE_TYPE_T state; |
|
||||
NE10_FFT_CFG_TYPE_T cfg = &state; |
|
||||
VARDECL(NE10_FFT_CPX_TYPE_T, buffer); |
|
||||
SAVE_STACK; |
|
||||
ALLOC(buffer, st->nfft, NE10_FFT_CPX_TYPE_T); |
|
||||
|
|
||||
if (!st->arch_fft->is_supported) { |
|
||||
/* This nfft length (scaled fft) not supported in NE10 */ |
|
||||
opus_ifft_c(st, fin, fout); |
|
||||
} |
|
||||
else { |
|
||||
memcpy((void *)cfg, st->arch_fft->priv, sizeof(NE10_FFT_STATE_TYPE_T)); |
|
||||
state.buffer = (NE10_FFT_CPX_TYPE_T *)&buffer[0]; |
|
||||
#if !defined(FIXED_POINT) |
|
||||
state.is_backward_scaled = 0; |
|
||||
|
|
||||
NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, |
|
||||
(NE10_FFT_CPX_TYPE_T *)fin, |
|
||||
cfg, 1); |
|
||||
#else |
|
||||
NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, |
|
||||
(NE10_FFT_CPX_TYPE_T *)fin, |
|
||||
cfg, 1, 0); |
|
||||
#endif |
|
||||
} |
|
||||
RESTORE_STACK; |
|
||||
} |
|
||||
@ -1,258 +0,0 @@ |
|||||
/* Copyright (c) 2015 Xiph.Org Foundation
|
|
||||
Written by Viswanath Puttagunta */ |
|
||||
/**
|
|
||||
@file celt_mdct_ne10.c |
|
||||
@brief ARM Neon optimizations for mdct using NE10 library |
|
||||
*/ |
|
||||
|
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef SKIP_CONFIG_H |
|
||||
#ifdef HAVE_CONFIG_H |
|
||||
#include "config.h" |
|
||||
#endif |
|
||||
#endif |
|
||||
|
|
||||
#include "kiss_fft.h" |
|
||||
#include "_kiss_fft_guts.h" |
|
||||
#include "mdct.h" |
|
||||
#include "stack_alloc.h" |
|
||||
|
|
||||
void clt_mdct_forward_neon(const mdct_lookup *l, |
|
||||
kiss_fft_scalar *in, |
|
||||
kiss_fft_scalar * OPUS_RESTRICT out, |
|
||||
const opus_val16 *window, |
|
||||
int overlap, int shift, int stride, int arch) |
|
||||
{ |
|
||||
int i; |
|
||||
int N, N2, N4; |
|
||||
VARDECL(kiss_fft_scalar, f); |
|
||||
VARDECL(kiss_fft_cpx, f2); |
|
||||
const kiss_fft_state *st = l->kfft[shift]; |
|
||||
const kiss_twiddle_scalar *trig; |
|
||||
|
|
||||
SAVE_STACK; |
|
||||
|
|
||||
N = l->n; |
|
||||
trig = l->trig; |
|
||||
for (i=0;i<shift;i++) |
|
||||
{ |
|
||||
N >>= 1; |
|
||||
trig += N; |
|
||||
} |
|
||||
N2 = N>>1; |
|
||||
N4 = N>>2; |
|
||||
|
|
||||
ALLOC(f, N2, kiss_fft_scalar); |
|
||||
ALLOC(f2, N4, kiss_fft_cpx); |
|
||||
|
|
||||
/* Consider the input to be composed of four blocks: [a, b, c, d] */ |
|
||||
/* Window, shuffle, fold */ |
|
||||
{ |
|
||||
/* Temp pointers to make it really clear to the compiler what we're doing */ |
|
||||
const kiss_fft_scalar * OPUS_RESTRICT xp1 = in+(overlap>>1); |
|
||||
const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+N2-1+(overlap>>1); |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp = f; |
|
||||
const opus_val16 * OPUS_RESTRICT wp1 = window+(overlap>>1); |
|
||||
const opus_val16 * OPUS_RESTRICT wp2 = window+(overlap>>1)-1; |
|
||||
for(i=0;i<((overlap+3)>>2);i++) |
|
||||
{ |
|
||||
/* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ |
|
||||
*yp++ = MULT16_32_Q15(*wp2, xp1[N2]) + MULT16_32_Q15(*wp1,*xp2); |
|
||||
*yp++ = MULT16_32_Q15(*wp1, *xp1) - MULT16_32_Q15(*wp2, xp2[-N2]); |
|
||||
xp1+=2; |
|
||||
xp2-=2; |
|
||||
wp1+=2; |
|
||||
wp2-=2; |
|
||||
} |
|
||||
wp1 = window; |
|
||||
wp2 = window+overlap-1; |
|
||||
for(;i<N4-((overlap+3)>>2);i++) |
|
||||
{ |
|
||||
/* Real part arranged as a-bR, Imag part arranged as -c-dR */ |
|
||||
*yp++ = *xp2; |
|
||||
*yp++ = *xp1; |
|
||||
xp1+=2; |
|
||||
xp2-=2; |
|
||||
} |
|
||||
for(;i<N4;i++) |
|
||||
{ |
|
||||
/* Real part arranged as a-bR, Imag part arranged as -c-dR */ |
|
||||
*yp++ = -MULT16_32_Q15(*wp1, xp1[-N2]) + MULT16_32_Q15(*wp2, *xp2); |
|
||||
*yp++ = MULT16_32_Q15(*wp2, *xp1) + MULT16_32_Q15(*wp1, xp2[N2]); |
|
||||
xp1+=2; |
|
||||
xp2-=2; |
|
||||
wp1+=2; |
|
||||
wp2-=2; |
|
||||
} |
|
||||
} |
|
||||
/* Pre-rotation */ |
|
||||
{ |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp = f; |
|
||||
const kiss_twiddle_scalar *t = &trig[0]; |
|
||||
for(i=0;i<N4;i++) |
|
||||
{ |
|
||||
kiss_fft_cpx yc; |
|
||||
kiss_twiddle_scalar t0, t1; |
|
||||
kiss_fft_scalar re, im, yr, yi; |
|
||||
t0 = t[i]; |
|
||||
t1 = t[N4+i]; |
|
||||
re = *yp++; |
|
||||
im = *yp++; |
|
||||
yr = S_MUL(re,t0) - S_MUL(im,t1); |
|
||||
yi = S_MUL(im,t0) + S_MUL(re,t1); |
|
||||
yc.r = yr; |
|
||||
yc.i = yi; |
|
||||
f2[i] = yc; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
opus_fft(st, f2, (kiss_fft_cpx *)f, arch); |
|
||||
|
|
||||
/* Post-rotate */ |
|
||||
{ |
|
||||
/* Temp pointers to make it really clear to the compiler what we're doing */ |
|
||||
const kiss_fft_cpx * OPUS_RESTRICT fp = (kiss_fft_cpx *)f; |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp1 = out; |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp2 = out+stride*(N2-1); |
|
||||
const kiss_twiddle_scalar *t = &trig[0]; |
|
||||
/* Temp pointers to make it really clear to the compiler what we're doing */ |
|
||||
for(i=0;i<N4;i++) |
|
||||
{ |
|
||||
kiss_fft_scalar yr, yi; |
|
||||
yr = S_MUL(fp->i,t[N4+i]) - S_MUL(fp->r,t[i]); |
|
||||
yi = S_MUL(fp->r,t[N4+i]) + S_MUL(fp->i,t[i]); |
|
||||
*yp1 = yr; |
|
||||
*yp2 = yi; |
|
||||
fp++; |
|
||||
yp1 += 2*stride; |
|
||||
yp2 -= 2*stride; |
|
||||
} |
|
||||
} |
|
||||
RESTORE_STACK; |
|
||||
} |
|
||||
|
|
||||
void clt_mdct_backward_neon(const mdct_lookup *l, |
|
||||
kiss_fft_scalar *in, |
|
||||
kiss_fft_scalar * OPUS_RESTRICT out, |
|
||||
const opus_val16 * OPUS_RESTRICT window, |
|
||||
int overlap, int shift, int stride, int arch) |
|
||||
{ |
|
||||
int i; |
|
||||
int N, N2, N4; |
|
||||
VARDECL(kiss_fft_scalar, f); |
|
||||
const kiss_twiddle_scalar *trig; |
|
||||
const kiss_fft_state *st = l->kfft[shift]; |
|
||||
|
|
||||
N = l->n; |
|
||||
trig = l->trig; |
|
||||
for (i=0;i<shift;i++) |
|
||||
{ |
|
||||
N >>= 1; |
|
||||
trig += N; |
|
||||
} |
|
||||
N2 = N>>1; |
|
||||
N4 = N>>2; |
|
||||
|
|
||||
ALLOC(f, N2, kiss_fft_scalar); |
|
||||
|
|
||||
/* Pre-rotate */ |
|
||||
{ |
|
||||
/* Temp pointers to make it really clear to the compiler what we're doing */ |
|
||||
const kiss_fft_scalar * OPUS_RESTRICT xp1 = in; |
|
||||
const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+stride*(N2-1); |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp = f; |
|
||||
const kiss_twiddle_scalar * OPUS_RESTRICT t = &trig[0]; |
|
||||
for(i=0;i<N4;i++) |
|
||||
{ |
|
||||
kiss_fft_scalar yr, yi; |
|
||||
yr = S_MUL(*xp2, t[i]) + S_MUL(*xp1, t[N4+i]); |
|
||||
yi = S_MUL(*xp1, t[i]) - S_MUL(*xp2, t[N4+i]); |
|
||||
yp[2*i] = yr; |
|
||||
yp[2*i+1] = yi; |
|
||||
xp1+=2*stride; |
|
||||
xp2-=2*stride; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
opus_ifft(st, (kiss_fft_cpx *)f, (kiss_fft_cpx*)(out+(overlap>>1)), arch); |
|
||||
|
|
||||
/* Post-rotate and de-shuffle from both ends of the buffer at once to make
|
|
||||
it in-place. */ |
|
||||
{ |
|
||||
kiss_fft_scalar * yp0 = out+(overlap>>1); |
|
||||
kiss_fft_scalar * yp1 = out+(overlap>>1)+N2-2; |
|
||||
const kiss_twiddle_scalar *t = &trig[0]; |
|
||||
/* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the
|
|
||||
middle pair will be computed twice. */ |
|
||||
for(i=0;i<(N4+1)>>1;i++) |
|
||||
{ |
|
||||
kiss_fft_scalar re, im, yr, yi; |
|
||||
kiss_twiddle_scalar t0, t1; |
|
||||
re = yp0[0]; |
|
||||
im = yp0[1]; |
|
||||
t0 = t[i]; |
|
||||
t1 = t[N4+i]; |
|
||||
/* We'd scale up by 2 here, but instead it's done when mixing the windows */ |
|
||||
yr = S_MUL(re,t0) + S_MUL(im,t1); |
|
||||
yi = S_MUL(re,t1) - S_MUL(im,t0); |
|
||||
re = yp1[0]; |
|
||||
im = yp1[1]; |
|
||||
yp0[0] = yr; |
|
||||
yp1[1] = yi; |
|
||||
|
|
||||
t0 = t[(N4-i-1)]; |
|
||||
t1 = t[(N2-i-1)]; |
|
||||
/* We'd scale up by 2 here, but instead it's done when mixing the windows */ |
|
||||
yr = S_MUL(re,t0) + S_MUL(im,t1); |
|
||||
yi = S_MUL(re,t1) - S_MUL(im,t0); |
|
||||
yp1[0] = yr; |
|
||||
yp0[1] = yi; |
|
||||
yp0 += 2; |
|
||||
yp1 -= 2; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Mirror on both sides for TDAC */ |
|
||||
{ |
|
||||
kiss_fft_scalar * OPUS_RESTRICT xp1 = out+overlap-1; |
|
||||
kiss_fft_scalar * OPUS_RESTRICT yp1 = out; |
|
||||
const opus_val16 * OPUS_RESTRICT wp1 = window; |
|
||||
const opus_val16 * OPUS_RESTRICT wp2 = window+overlap-1; |
|
||||
|
|
||||
for(i = 0; i < overlap/2; i++) |
|
||||
{ |
|
||||
kiss_fft_scalar x1, x2; |
|
||||
x1 = *xp1; |
|
||||
x2 = *yp1; |
|
||||
*yp1++ = MULT16_32_Q15(*wp2, x2) - MULT16_32_Q15(*wp1, x1); |
|
||||
*xp1-- = MULT16_32_Q15(*wp1, x2) + MULT16_32_Q15(*wp2, x1); |
|
||||
wp1++; |
|
||||
wp2--; |
|
||||
} |
|
||||
} |
|
||||
RESTORE_STACK; |
|
||||
} |
|
||||
@ -1,272 +0,0 @@ |
|||||
/* Copyright (c) 2014-2015 Xiph.Org Foundation
|
|
||||
Written by Viswanath Puttagunta */ |
|
||||
/**
|
|
||||
@file celt_neon_intr.c |
|
||||
@brief ARM Neon Intrinsic optimizations for celt |
|
||||
*/ |
|
||||
|
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifdef HAVE_CONFIG_H |
|
||||
#include "config.h" |
|
||||
#endif |
|
||||
|
|
||||
#include <arm_neon.h> |
|
||||
#include "../pitch.h" |
|
||||
|
|
||||
#if defined(FIXED_POINT) |
|
||||
#include <string.h> |
|
||||
|
|
||||
void xcorr_kernel_neon_fixed(const opus_val16 * x, const opus_val16 * y, opus_val32 sum[4], int len) |
|
||||
{ |
|
||||
int j; |
|
||||
int32x4_t a = vld1q_s32(sum); |
|
||||
/* Load y[0...3] */ |
|
||||
/* This requires len>0 to always be valid (which we assert in the C code). */ |
|
||||
int16x4_t y0 = vld1_s16(y); |
|
||||
y += 4; |
|
||||
|
|
||||
/* This loop loads one y value more than we actually need.
|
|
||||
Therefore we have to stop as soon as there are 8 or fewer samples left |
|
||||
(instead of 7), to avoid reading past the end of the array. */ |
|
||||
for (j = 0; j + 8 < len; j += 8) |
|
||||
{ |
|
||||
/* Load x[0...7] */ |
|
||||
int16x8_t xx = vld1q_s16(x); |
|
||||
int16x4_t x0 = vget_low_s16(xx); |
|
||||
int16x4_t x4 = vget_high_s16(xx); |
|
||||
/* Load y[4...11] */ |
|
||||
int16x8_t yy = vld1q_s16(y); |
|
||||
int16x4_t y4 = vget_low_s16(yy); |
|
||||
int16x4_t y8 = vget_high_s16(yy); |
|
||||
int32x4_t a0 = vmlal_lane_s16(a, y0, x0, 0); |
|
||||
int32x4_t a1 = vmlal_lane_s16(a0, y4, x4, 0); |
|
||||
|
|
||||
int16x4_t y1 = vext_s16(y0, y4, 1); |
|
||||
int16x4_t y5 = vext_s16(y4, y8, 1); |
|
||||
int32x4_t a2 = vmlal_lane_s16(a1, y1, x0, 1); |
|
||||
int32x4_t a3 = vmlal_lane_s16(a2, y5, x4, 1); |
|
||||
|
|
||||
int16x4_t y2 = vext_s16(y0, y4, 2); |
|
||||
int16x4_t y6 = vext_s16(y4, y8, 2); |
|
||||
int32x4_t a4 = vmlal_lane_s16(a3, y2, x0, 2); |
|
||||
int32x4_t a5 = vmlal_lane_s16(a4, y6, x4, 2); |
|
||||
|
|
||||
int16x4_t y3 = vext_s16(y0, y4, 3); |
|
||||
int16x4_t y7 = vext_s16(y4, y8, 3); |
|
||||
int32x4_t a6 = vmlal_lane_s16(a5, y3, x0, 3); |
|
||||
int32x4_t a7 = vmlal_lane_s16(a6, y7, x4, 3); |
|
||||
|
|
||||
y0 = y8; |
|
||||
a = a7; |
|
||||
x += 8; |
|
||||
y += 8; |
|
||||
} |
|
||||
if (j + 4 < len) { |
|
||||
/* Load x[0...3] */ |
|
||||
int16x4_t x0 = vld1_s16(x); |
|
||||
/* Load y[4...7] */ |
|
||||
int16x4_t y4 = vld1_s16(y); |
|
||||
int32x4_t a0 = vmlal_lane_s16(a, y0, x0, 0); |
|
||||
int16x4_t y1 = vext_s16(y0, y4, 1); |
|
||||
int32x4_t a1 = vmlal_lane_s16(a0, y1, x0, 1); |
|
||||
int16x4_t y2 = vext_s16(y0, y4, 2); |
|
||||
int32x4_t a2 = vmlal_lane_s16(a1, y2, x0, 2); |
|
||||
int16x4_t y3 = vext_s16(y0, y4, 3); |
|
||||
int32x4_t a3 = vmlal_lane_s16(a2, y3, x0, 3); |
|
||||
y0 = y4; |
|
||||
a = a3; |
|
||||
x += 4; |
|
||||
y += 4; |
|
||||
j += 4; |
|
||||
} |
|
||||
if (j + 2 < len) { |
|
||||
/* Load x[0...1] */ |
|
||||
int16x4x2_t xx = vld2_dup_s16(x); |
|
||||
int16x4_t x0 = xx.val[0]; |
|
||||
int16x4_t x1 = xx.val[1]; |
|
||||
/* Load y[4...5].
|
|
||||
We would like to use vld1_dup_s32(), but casting the pointer would |
|
||||
break strict aliasing rules and potentially have alignment issues. |
|
||||
Fortunately the compiler seems capable of translating this memcpy() |
|
||||
and vdup_n_s32() into the equivalent vld1_dup_s32().*/ |
|
||||
int32_t yy; |
|
||||
memcpy(&yy, y, sizeof(yy)); |
|
||||
int16x4_t y4 = vreinterpret_s16_s32(vdup_n_s32(yy)); |
|
||||
int32x4_t a0 = vmlal_s16(a, y0, x0); |
|
||||
int16x4_t y1 = vext_s16(y0, y4, 1); |
|
||||
/* Replace bottom copy of {y[5], y[4]} in y4 with {y[3], y[2]} from y0,
|
|
||||
using VSRI instead of VEXT, since it's a data-processing |
|
||||
instruction. */ |
|
||||
y0 = vreinterpret_s16_s64(vsri_n_s64(vreinterpret_s64_s16(y4), |
|
||||
vreinterpret_s64_s16(y0), 32)); |
|
||||
int32x4_t a1 = vmlal_s16(a0, y1, x1); |
|
||||
a = a1; |
|
||||
x += 2; |
|
||||
y += 2; |
|
||||
j += 2; |
|
||||
} |
|
||||
if (j + 1 < len) { |
|
||||
/* Load next x. */ |
|
||||
int16x4_t x0 = vld1_dup_s16(x); |
|
||||
int32x4_t a0 = vmlal_s16(a, y0, x0); |
|
||||
/* Load last y. */ |
|
||||
int16x4_t y4 = vld1_dup_s16(y); |
|
||||
y0 = vreinterpret_s16_s64(vsri_n_s64(vreinterpret_s64_s16(y4), |
|
||||
vreinterpret_s64_s16(y0), 16)); |
|
||||
a = a0; |
|
||||
x++; |
|
||||
} |
|
||||
/* Load last x. */ |
|
||||
int16x4_t x0 = vld1_dup_s16(x); |
|
||||
int32x4_t a0 = vmlal_s16(a, y0, x0); |
|
||||
vst1q_s32(sum, a0); |
|
||||
} |
|
||||
|
|
||||
#else |
|
||||
|
|
||||
#if defined(__ARM_FEATURE_FMA) && defined(__ARM_ARCH_ISA_A64) |
|
||||
/* If we can, force the compiler to use an FMA instruction rather than break
|
|
||||
* vmlaq_f32() into fmul/fadd. */ |
|
||||
#ifdef vmlaq_lane_f32 |
|
||||
#undef vmlaq_lane_f32 |
|
||||
#endif |
|
||||
#define vmlaq_lane_f32(a,b,c,lane) vfmaq_lane_f32(a,b,c,lane) |
|
||||
#endif |
|
||||
|
|
||||
|
|
||||
/*
|
|
||||
* Function: xcorr_kernel_neon_float |
|
||||
* --------------------------------- |
|
||||
* Computes 4 correlation values and stores them in sum[4] |
|
||||
*/ |
|
||||
static void xcorr_kernel_neon_float(const float32_t *x, const float32_t *y, |
|
||||
float32_t sum[4], int len) { |
|
||||
float32x4_t YY[3]; |
|
||||
float32x4_t YEXT[3]; |
|
||||
float32x4_t XX[2]; |
|
||||
float32x2_t XX_2; |
|
||||
float32x4_t SUMM; |
|
||||
const float32_t *xi = x; |
|
||||
const float32_t *yi = y; |
|
||||
|
|
||||
celt_assert(len>0); |
|
||||
|
|
||||
YY[0] = vld1q_f32(yi); |
|
||||
SUMM = vdupq_n_f32(0); |
|
||||
|
|
||||
/* Consume 8 elements in x vector and 12 elements in y
|
|
||||
* vector. However, the 12'th element never really gets |
|
||||
* touched in this loop. So, if len == 8, then we only |
|
||||
* must access y[0] to y[10]. y[11] must not be accessed |
|
||||
* hence make sure len > 8 and not len >= 8 |
|
||||
*/ |
|
||||
while (len > 8) { |
|
||||
yi += 4; |
|
||||
YY[1] = vld1q_f32(yi); |
|
||||
yi += 4; |
|
||||
YY[2] = vld1q_f32(yi); |
|
||||
|
|
||||
XX[0] = vld1q_f32(xi); |
|
||||
xi += 4; |
|
||||
XX[1] = vld1q_f32(xi); |
|
||||
xi += 4; |
|
||||
|
|
||||
SUMM = vmlaq_lane_f32(SUMM, YY[0], vget_low_f32(XX[0]), 0); |
|
||||
YEXT[0] = vextq_f32(YY[0], YY[1], 1); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[0]), 1); |
|
||||
YEXT[1] = vextq_f32(YY[0], YY[1], 2); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[0]), 0); |
|
||||
YEXT[2] = vextq_f32(YY[0], YY[1], 3); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[0]), 1); |
|
||||
|
|
||||
SUMM = vmlaq_lane_f32(SUMM, YY[1], vget_low_f32(XX[1]), 0); |
|
||||
YEXT[0] = vextq_f32(YY[1], YY[2], 1); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[1]), 1); |
|
||||
YEXT[1] = vextq_f32(YY[1], YY[2], 2); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[1]), 0); |
|
||||
YEXT[2] = vextq_f32(YY[1], YY[2], 3); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[1]), 1); |
|
||||
|
|
||||
YY[0] = YY[2]; |
|
||||
len -= 8; |
|
||||
} |
|
||||
|
|
||||
/* Consume 4 elements in x vector and 8 elements in y
|
|
||||
* vector. However, the 8'th element in y never really gets |
|
||||
* touched in this loop. So, if len == 4, then we only |
|
||||
* must access y[0] to y[6]. y[7] must not be accessed |
|
||||
* hence make sure len>4 and not len>=4 |
|
||||
*/ |
|
||||
if (len > 4) { |
|
||||
yi += 4; |
|
||||
YY[1] = vld1q_f32(yi); |
|
||||
|
|
||||
XX[0] = vld1q_f32(xi); |
|
||||
xi += 4; |
|
||||
|
|
||||
SUMM = vmlaq_lane_f32(SUMM, YY[0], vget_low_f32(XX[0]), 0); |
|
||||
YEXT[0] = vextq_f32(YY[0], YY[1], 1); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[0]), 1); |
|
||||
YEXT[1] = vextq_f32(YY[0], YY[1], 2); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[0]), 0); |
|
||||
YEXT[2] = vextq_f32(YY[0], YY[1], 3); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[0]), 1); |
|
||||
|
|
||||
YY[0] = YY[1]; |
|
||||
len -= 4; |
|
||||
} |
|
||||
|
|
||||
while (--len > 0) { |
|
||||
XX_2 = vld1_dup_f32(xi++); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YY[0], XX_2, 0); |
|
||||
YY[0]= vld1q_f32(++yi); |
|
||||
} |
|
||||
|
|
||||
XX_2 = vld1_dup_f32(xi); |
|
||||
SUMM = vmlaq_lane_f32(SUMM, YY[0], XX_2, 0); |
|
||||
|
|
||||
vst1q_f32(sum, SUMM); |
|
||||
} |
|
||||
|
|
||||
void celt_pitch_xcorr_float_neon(const opus_val16 *_x, const opus_val16 *_y, |
|
||||
opus_val32 *xcorr, int len, int max_pitch, int arch) { |
|
||||
int i; |
|
||||
(void)arch; |
|
||||
celt_assert(max_pitch > 0); |
|
||||
celt_sig_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0); |
|
||||
|
|
||||
for (i = 0; i < (max_pitch-3); i += 4) { |
|
||||
xcorr_kernel_neon_float((const float32_t *)_x, (const float32_t *)_y+i, |
|
||||
(float32_t *)xcorr+i, len); |
|
||||
} |
|
||||
|
|
||||
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ |
|
||||
for (; i < max_pitch; i++) { |
|
||||
xcorr[i] = celt_inner_prod_neon(_x, _y+i, len); |
|
||||
} |
|
||||
} |
|
||||
#endif |
|
||||
@ -1,555 +0,0 @@ |
|||||
.syntax unified |
|
||||
@ Copyright (c) 2007-2008 CSIRO |
|
||||
@ Copyright (c) 2007-2009 Xiph.Org Foundation |
|
||||
@ Copyright (c) 2013 Parrot |
|
||||
@ Written by Aurélien Zanelli |
|
||||
@ |
|
||||
@ 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. |
|
||||
|
|
||||
.text; .p2align 2; .arch armv7-a |
|
||||
.fpu neon |
|
||||
.object_arch armv4t |
|
||||
|
|
||||
.include "celt/arm/armopts-gnu.S" |
|
||||
|
|
||||
.if OPUS_ARM_MAY_HAVE_EDSP |
|
||||
.global celt_pitch_xcorr_edsp |
|
||||
.endif |
|
||||
|
|
||||
.if OPUS_ARM_MAY_HAVE_NEON |
|
||||
.global celt_pitch_xcorr_neon |
|
||||
.endif |
|
||||
|
|
||||
.if OPUS_ARM_MAY_HAVE_NEON |
|
||||
|
|
||||
@ Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3 |
|
||||
.type xcorr_kernel_neon, %function; xcorr_kernel_neon: @ PROC |
|
||||
xcorr_kernel_neon_start: |
|
||||
@ input: |
|
||||
@ r3 = int len |
|
||||
@ r4 = opus_val16 *x |
|
||||
@ r5 = opus_val16 *y |
|
||||
@ q0 = opus_val32 sum[4] |
|
||||
@ output: |
|
||||
@ q0 = opus_val32 sum[4] |
|
||||
@ preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15 |
|
||||
@ internal usage: |
|
||||
@ r12 = int j |
|
||||
@ d3 = y_3|y_2|y_1|y_0 |
|
||||
@ q2 = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4 |
|
||||
@ q3 = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0 |
|
||||
@ q8 = scratch |
|
||||
@ |
|
||||
@ Load y[0...3] |
|
||||
@ This requires len>0 to always be valid (which we assert in the C code). |
|
||||
VLD1.16 {d5}, [r5]! |
|
||||
SUBS r12, r3, #8 |
|
||||
BLE xcorr_kernel_neon_process4 |
|
||||
@ Process 8 samples at a time. |
|
||||
@ This loop loads one y value more than we actually need. Therefore we have to |
|
||||
@ stop as soon as there are 8 or fewer samples left (instead of 7), to avoid |
|
||||
@ reading past the end of the array. |
|
||||
xcorr_kernel_neon_process8: |
|
||||
@ This loop has 19 total instructions (10 cycles to issue, minimum), with |
|
||||
@ - 2 cycles of ARM insrtuctions, |
|
||||
@ - 10 cycles of load/store/byte permute instructions, and |
|
||||
@ - 9 cycles of data processing instructions. |
|
||||
@ On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the |
|
||||
@ latter two categories, meaning the whole loop should run in 10 cycles per |
|
||||
@ iteration, barring cache misses. |
|
||||
@ |
|
||||
@ Load x[0...7] |
|
||||
VLD1.16 {d6, d7}, [r4]! |
|
||||
@ Unlike VMOV, VAND is a data processsing instruction (and doesn't get |
|
||||
@ assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. |
|
||||
VAND d3, d5, d5 |
|
||||
SUBS r12, r12, #8 |
|
||||
@ Load y[4...11] |
|
||||
VLD1.16 {d4, d5}, [r5]! |
|
||||
VMLAL.S16 q0, d3, d6[0] |
|
||||
VEXT.16 d16, d3, d4, #1 |
|
||||
VMLAL.S16 q0, d4, d7[0] |
|
||||
VEXT.16 d17, d4, d5, #1 |
|
||||
VMLAL.S16 q0, d16, d6[1] |
|
||||
VEXT.16 d16, d3, d4, #2 |
|
||||
VMLAL.S16 q0, d17, d7[1] |
|
||||
VEXT.16 d17, d4, d5, #2 |
|
||||
VMLAL.S16 q0, d16, d6[2] |
|
||||
VEXT.16 d16, d3, d4, #3 |
|
||||
VMLAL.S16 q0, d17, d7[2] |
|
||||
VEXT.16 d17, d4, d5, #3 |
|
||||
VMLAL.S16 q0, d16, d6[3] |
|
||||
VMLAL.S16 q0, d17, d7[3] |
|
||||
BGT xcorr_kernel_neon_process8 |
|
||||
@ Process 4 samples here if we have > 4 left (still reading one extra y value). |
|
||||
xcorr_kernel_neon_process4: |
|
||||
ADDS r12, r12, #4 |
|
||||
BLE xcorr_kernel_neon_process2 |
|
||||
@ Load x[0...3] |
|
||||
VLD1.16 d6, [r4]! |
|
||||
@ Use VAND since it's a data processing instruction again. |
|
||||
VAND d4, d5, d5 |
|
||||
SUB r12, r12, #4 |
|
||||
@ Load y[4...7] |
|
||||
VLD1.16 d5, [r5]! |
|
||||
VMLAL.S16 q0, d4, d6[0] |
|
||||
VEXT.16 d16, d4, d5, #1 |
|
||||
VMLAL.S16 q0, d16, d6[1] |
|
||||
VEXT.16 d16, d4, d5, #2 |
|
||||
VMLAL.S16 q0, d16, d6[2] |
|
||||
VEXT.16 d16, d4, d5, #3 |
|
||||
VMLAL.S16 q0, d16, d6[3] |
|
||||
@ Process 2 samples here if we have > 2 left (still reading one extra y value). |
|
||||
xcorr_kernel_neon_process2: |
|
||||
ADDS r12, r12, #2 |
|
||||
BLE xcorr_kernel_neon_process1 |
|
||||
@ Load x[0...1] |
|
||||
VLD2.16 {d6[],d7[]}, [r4]! |
|
||||
@ Use VAND since it's a data processing instruction again. |
|
||||
VAND d4, d5, d5 |
|
||||
SUB r12, r12, #2 |
|
||||
@ Load y[4...5] |
|
||||
VLD1.32 {d5[]}, [r5]! |
|
||||
VMLAL.S16 q0, d4, d6 |
|
||||
VEXT.16 d16, d4, d5, #1 |
|
||||
@ Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI |
|
||||
@ instead of VEXT, since it's a data-processing instruction. |
|
||||
VSRI.64 d5, d4, #32 |
|
||||
VMLAL.S16 q0, d16, d7 |
|
||||
@ Process 1 sample using the extra y value we loaded above. |
|
||||
xcorr_kernel_neon_process1: |
|
||||
@ Load next *x |
|
||||
VLD1.16 {d6[]}, [r4]! |
|
||||
ADDS r12, r12, #1 |
|
||||
@ y[0...3] are left in d5 from prior iteration(s) (if any) |
|
||||
VMLAL.S16 q0, d5, d6 |
|
||||
MOVLE pc, lr |
|
||||
@ Now process 1 last sample, not reading ahead. |
|
||||
@ Load last *y |
|
||||
VLD1.16 {d4[]}, [r5]! |
|
||||
VSRI.64 d4, d5, #16 |
|
||||
@ Load last *x |
|
||||
VLD1.16 {d6[]}, [r4]! |
|
||||
VMLAL.S16 q0, d4, d6 |
|
||||
MOV pc, lr |
|
||||
.size xcorr_kernel_neon, .-xcorr_kernel_neon @ ENDP |
|
||||
|
|
||||
@ opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y, |
|
||||
@ opus_val32 *xcorr, int len, int max_pitch, int arch) |
|
||||
.type celt_pitch_xcorr_neon, %function; celt_pitch_xcorr_neon: @ PROC |
|
||||
@ input: |
|
||||
@ r0 = opus_val16 *_x |
|
||||
@ r1 = opus_val16 *_y |
|
||||
@ r2 = opus_val32 *xcorr |
|
||||
@ r3 = int len |
|
||||
@ output: |
|
||||
@ r0 = int maxcorr |
|
||||
@ internal usage: |
|
||||
@ r4 = opus_val16 *x (for xcorr_kernel_neon()) |
|
||||
@ r5 = opus_val16 *y (for xcorr_kernel_neon()) |
|
||||
@ r6 = int max_pitch |
|
||||
@ r12 = int j |
|
||||
@ q15 = int maxcorr[4] (q15 is not used by xcorr_kernel_neon()) |
|
||||
@ ignored: |
|
||||
@ int arch |
|
||||
STMFD sp!, {r4-r6, lr} |
|
||||
LDR r6, [sp, #16] |
|
||||
VMOV.S32 q15, #1 |
|
||||
@ if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done |
|
||||
SUBS r6, r6, #4 |
|
||||
BLT celt_pitch_xcorr_neon_process4_done |
|
||||
celt_pitch_xcorr_neon_process4: |
|
||||
@ xcorr_kernel_neon parameters: |
|
||||
@ r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0} |
|
||||
MOV r4, r0 |
|
||||
MOV r5, r1 |
|
||||
VEOR q0, q0, q0 |
|
||||
@ xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3. |
|
||||
@ So we don't save/restore any other registers. |
|
||||
BL xcorr_kernel_neon_start |
|
||||
SUBS r6, r6, #4 |
|
||||
VST1.32 {q0}, [r2]! |
|
||||
@ _y += 4 |
|
||||
ADD r1, r1, #8 |
|
||||
VMAX.S32 q15, q15, q0 |
|
||||
@ if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done |
|
||||
BGE celt_pitch_xcorr_neon_process4 |
|
||||
@ We have less than 4 sums left to compute. |
|
||||
celt_pitch_xcorr_neon_process4_done: |
|
||||
ADDS r6, r6, #4 |
|
||||
@ Reduce maxcorr to a single value |
|
||||
VMAX.S32 d30, d30, d31 |
|
||||
VPMAX.S32 d30, d30, d30 |
|
||||
@ if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done |
|
||||
BLE celt_pitch_xcorr_neon_done |
|
||||
@ Now compute each remaining sum one at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining: |
|
||||
MOV r4, r0 |
|
||||
MOV r5, r1 |
|
||||
VMOV.I32 q0, #0 |
|
||||
SUBS r12, r3, #8 |
|
||||
BLT celt_pitch_xcorr_neon_process_remaining4 |
|
||||
@ Sum terms 8 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop8: |
|
||||
@ Load x[0...7] |
|
||||
VLD1.16 {q1}, [r4]! |
|
||||
@ Load y[0...7] |
|
||||
VLD1.16 {q2}, [r5]! |
|
||||
SUBS r12, r12, #8 |
|
||||
VMLAL.S16 q0, d4, d2 |
|
||||
VMLAL.S16 q0, d5, d3 |
|
||||
BGE celt_pitch_xcorr_neon_process_remaining_loop8 |
|
||||
@ Sum terms 4 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining4: |
|
||||
ADDS r12, r12, #4 |
|
||||
BLT celt_pitch_xcorr_neon_process_remaining4_done |
|
||||
@ Load x[0...3] |
|
||||
VLD1.16 {d2}, [r4]! |
|
||||
@ Load y[0...3] |
|
||||
VLD1.16 {d3}, [r5]! |
|
||||
SUB r12, r12, #4 |
|
||||
VMLAL.S16 q0, d3, d2 |
|
||||
celt_pitch_xcorr_neon_process_remaining4_done: |
|
||||
@ Reduce the sum to a single value. |
|
||||
VADD.S32 d0, d0, d1 |
|
||||
VPADDL.S32 d0, d0 |
|
||||
ADDS r12, r12, #4 |
|
||||
BLE celt_pitch_xcorr_neon_process_remaining_loop_done |
|
||||
@ Sum terms 1 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop1: |
|
||||
VLD1.16 {d2[]}, [r4]! |
|
||||
VLD1.16 {d3[]}, [r5]! |
|
||||
SUBS r12, r12, #1 |
|
||||
VMLAL.S16 q0, d2, d3 |
|
||||
BGT celt_pitch_xcorr_neon_process_remaining_loop1 |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop_done: |
|
||||
VST1.32 {d0[0]}, [r2]! |
|
||||
VMAX.S32 d30, d30, d0 |
|
||||
SUBS r6, r6, #1 |
|
||||
@ _y++ |
|
||||
ADD r1, r1, #2 |
|
||||
@ if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining |
|
||||
BGT celt_pitch_xcorr_neon_process_remaining |
|
||||
celt_pitch_xcorr_neon_done: |
|
||||
VMOV.32 r0, d30[0] |
|
||||
LDMFD sp!, {r4-r6, pc} |
|
||||
.size celt_pitch_xcorr_neon, .-celt_pitch_xcorr_neon @ ENDP |
|
||||
|
|
||||
.endif |
|
||||
|
|
||||
.if OPUS_ARM_MAY_HAVE_EDSP |
|
||||
|
|
||||
@ This will get used on ARMv7 devices without NEON, so it has been optimized |
|
||||
@ to take advantage of dual-issuing where possible. |
|
||||
.type xcorr_kernel_edsp, %function; xcorr_kernel_edsp: @ PROC |
|
||||
xcorr_kernel_edsp_start: |
|
||||
@ input: |
|
||||
@ r3 = int len |
|
||||
@ r4 = opus_val16 *_x (must be 32-bit aligned) |
|
||||
@ r5 = opus_val16 *_y (must be 32-bit aligned) |
|
||||
@ r6...r9 = opus_val32 sum[4] |
|
||||
@ output: |
|
||||
@ r6...r9 = opus_val32 sum[4] |
|
||||
@ preserved: r0-r5 |
|
||||
@ internal usage |
|
||||
@ r2 = int j |
|
||||
@ r12,r14 = opus_val16 x[4] |
|
||||
@ r10,r11 = opus_val16 y[4] |
|
||||
STMFD sp!, {r2,r4,r5,lr} |
|
||||
LDR r10, [r5], #4 @ Load y[0...1] |
|
||||
SUBS r2, r3, #4 @ j = len-4 |
|
||||
LDR r11, [r5], #4 @ Load y[2...3] |
|
||||
BLE xcorr_kernel_edsp_process4_done |
|
||||
LDR r12, [r4], #4 @ Load x[0...1] |
|
||||
@ Stall |
|
||||
xcorr_kernel_edsp_process4: |
|
||||
@ The multiplies must issue from pipeline 0, and can't dual-issue with each |
|
||||
@ other. Every other instruction here dual-issues with a multiply, and is |
|
||||
@ thus "free". There should be no stalls in the body of the loop. |
|
||||
SMLABB r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x_0,y_0) |
|
||||
LDR r14, [r4], #4 @ Load x[2...3] |
|
||||
SMLABT r7, r12, r10, r7 @ sum[1] = MAC16_16(sum[1],x_0,y_1) |
|
||||
SUBS r2, r2, #4 @ j-=4 |
|
||||
SMLABB r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x_0,y_2) |
|
||||
SMLABT r9, r12, r11, r9 @ sum[3] = MAC16_16(sum[3],x_0,y_3) |
|
||||
SMLATT r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x_1,y_1) |
|
||||
LDR r10, [r5], #4 @ Load y[4...5] |
|
||||
SMLATB r7, r12, r11, r7 @ sum[1] = MAC16_16(sum[1],x_1,y_2) |
|
||||
SMLATT r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x_1,y_3) |
|
||||
SMLATB r9, r12, r10, r9 @ sum[3] = MAC16_16(sum[3],x_1,y_4) |
|
||||
LDRGT r12, [r4], #4 @ Load x[0...1] |
|
||||
SMLABB r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],x_2,y_2) |
|
||||
SMLABT r7, r14, r11, r7 @ sum[1] = MAC16_16(sum[1],x_2,y_3) |
|
||||
SMLABB r8, r14, r10, r8 @ sum[2] = MAC16_16(sum[2],x_2,y_4) |
|
||||
SMLABT r9, r14, r10, r9 @ sum[3] = MAC16_16(sum[3],x_2,y_5) |
|
||||
SMLATT r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],x_3,y_3) |
|
||||
LDR r11, [r5], #4 @ Load y[6...7] |
|
||||
SMLATB r7, r14, r10, r7 @ sum[1] = MAC16_16(sum[1],x_3,y_4) |
|
||||
SMLATT r8, r14, r10, r8 @ sum[2] = MAC16_16(sum[2],x_3,y_5) |
|
||||
SMLATB r9, r14, r11, r9 @ sum[3] = MAC16_16(sum[3],x_3,y_6) |
|
||||
BGT xcorr_kernel_edsp_process4 |
|
||||
xcorr_kernel_edsp_process4_done: |
|
||||
ADDS r2, r2, #4 |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
LDRH r12, [r4], #2 @ r12 = *x++ |
|
||||
SUBS r2, r2, #1 @ j-- |
|
||||
@ Stall |
|
||||
SMLABB r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x,y_0) |
|
||||
LDRHGT r14, [r4], #2 @ r14 = *x++ |
|
||||
SMLABT r7, r12, r10, r7 @ sum[1] = MAC16_16(sum[1],x,y_1) |
|
||||
SMLABB r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x,y_2) |
|
||||
SMLABT r9, r12, r11, r9 @ sum[3] = MAC16_16(sum[3],x,y_3) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABT r6, r14, r10, r6 @ sum[0] = MAC16_16(sum[0],x,y_1) |
|
||||
SUBS r2, r2, #1 @ j-- |
|
||||
SMLABB r7, r14, r11, r7 @ sum[1] = MAC16_16(sum[1],x,y_2) |
|
||||
LDRH r10, [r5], #2 @ r10 = y_4 = *y++ |
|
||||
SMLABT r8, r14, r11, r8 @ sum[2] = MAC16_16(sum[2],x,y_3) |
|
||||
LDRHGT r12, [r4], #2 @ r12 = *x++ |
|
||||
SMLABB r9, r14, r10, r9 @ sum[3] = MAC16_16(sum[3],x,y_4) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABB r6, r12, r11, r6 @ sum[0] = MAC16_16(sum[0],tmp,y_2) |
|
||||
CMP r2, #1 @ j-- |
|
||||
SMLABT r7, r12, r11, r7 @ sum[1] = MAC16_16(sum[1],tmp,y_3) |
|
||||
LDRH r2, [r5], #2 @ r2 = y_5 = *y++ |
|
||||
SMLABB r8, r12, r10, r8 @ sum[2] = MAC16_16(sum[2],tmp,y_4) |
|
||||
LDRHGT r14, [r4] @ r14 = *x |
|
||||
SMLABB r9, r12, r2, r9 @ sum[3] = MAC16_16(sum[3],tmp,y_5) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABT r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],tmp,y_3) |
|
||||
LDRH r11, [r5] @ r11 = y_6 = *y |
|
||||
SMLABB r7, r14, r10, r7 @ sum[1] = MAC16_16(sum[1],tmp,y_4) |
|
||||
SMLABB r8, r14, r2, r8 @ sum[2] = MAC16_16(sum[2],tmp,y_5) |
|
||||
SMLABB r9, r14, r11, r9 @ sum[3] = MAC16_16(sum[3],tmp,y_6) |
|
||||
xcorr_kernel_edsp_done: |
|
||||
LDMFD sp!, {r2,r4,r5,pc} |
|
||||
.size xcorr_kernel_edsp, .-xcorr_kernel_edsp @ ENDP |
|
||||
|
|
||||
.type celt_pitch_xcorr_edsp, %function; celt_pitch_xcorr_edsp: @ PROC |
|
||||
@ input: |
|
||||
@ r0 = opus_val16 *_x (must be 32-bit aligned) |
|
||||
@ r1 = opus_val16 *_y (only needs to be 16-bit aligned) |
|
||||
@ r2 = opus_val32 *xcorr |
|
||||
@ r3 = int len |
|
||||
@ output: |
|
||||
@ r0 = maxcorr |
|
||||
@ internal usage |
|
||||
@ r4 = opus_val16 *x |
|
||||
@ r5 = opus_val16 *y |
|
||||
@ r6 = opus_val32 sum0 |
|
||||
@ r7 = opus_val32 sum1 |
|
||||
@ r8 = opus_val32 sum2 |
|
||||
@ r9 = opus_val32 sum3 |
|
||||
@ r1 = int max_pitch |
|
||||
@ r12 = int j |
|
||||
@ ignored: |
|
||||
@ int arch |
|
||||
STMFD sp!, {r4-r11, lr} |
|
||||
MOV r5, r1 |
|
||||
LDR r1, [sp, #36] |
|
||||
MOV r4, r0 |
|
||||
TST r5, #3 |
|
||||
@ maxcorr = 1 |
|
||||
MOV r0, #1 |
|
||||
BEQ celt_pitch_xcorr_edsp_process1u_done |
|
||||
@ Compute one sum at the start to make y 32-bit aligned. |
|
||||
SUBS r12, r3, #4 |
|
||||
@ r14 = sum = 0 |
|
||||
MOV r14, #0 |
|
||||
LDRH r8, [r5], #2 |
|
||||
BLE celt_pitch_xcorr_edsp_process1u_loop4_done |
|
||||
LDR r6, [r4], #4 |
|
||||
MOV r8, r8, LSL #16 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop4: |
|
||||
LDR r9, [r5], #4 |
|
||||
SMLABT r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) |
|
||||
LDR r7, [r4], #4 |
|
||||
SMLATB r14, r6, r9, r14 @ sum = MAC16_16(sum, x_1, y_1) |
|
||||
LDR r8, [r5], #4 |
|
||||
SMLABT r14, r7, r9, r14 @ sum = MAC16_16(sum, x_2, y_2) |
|
||||
SUBS r12, r12, #4 @ j-=4 |
|
||||
SMLATB r14, r7, r8, r14 @ sum = MAC16_16(sum, x_3, y_3) |
|
||||
LDRGT r6, [r4], #4 |
|
||||
BGT celt_pitch_xcorr_edsp_process1u_loop4 |
|
||||
MOV r8, r8, LSR #16 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop4_done: |
|
||||
ADDS r12, r12, #4 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop1: |
|
||||
LDRHGE r6, [r4], #2 |
|
||||
@ Stall |
|
||||
SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, *x, *y) |
|
||||
SUBSGE r12, r12, #1 |
|
||||
LDRHGT r8, [r5], #2 |
|
||||
BGT celt_pitch_xcorr_edsp_process1u_loop1 |
|
||||
@ Restore _x |
|
||||
SUB r4, r4, r3, LSL #1 |
|
||||
@ Restore and advance _y |
|
||||
SUB r5, r5, r3, LSL #1 |
|
||||
@ maxcorr = max(maxcorr, sum) |
|
||||
CMP r0, r14 |
|
||||
ADD r5, r5, #2 |
|
||||
MOVLT r0, r14 |
|
||||
SUBS r1, r1, #1 |
|
||||
@ xcorr[i] = sum |
|
||||
STR r14, [r2], #4 |
|
||||
BLE celt_pitch_xcorr_edsp_done |
|
||||
celt_pitch_xcorr_edsp_process1u_done: |
|
||||
@ if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process2 |
|
||||
SUBS r1, r1, #4 |
|
||||
BLT celt_pitch_xcorr_edsp_process2 |
|
||||
celt_pitch_xcorr_edsp_process4: |
|
||||
@ xcorr_kernel_edsp parameters: |
|
||||
@ r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0} |
|
||||
MOV r6, #0 |
|
||||
MOV r7, #0 |
|
||||
MOV r8, #0 |
|
||||
MOV r9, #0 |
|
||||
BL xcorr_kernel_edsp_start @ xcorr_kernel_edsp(_x, _y+i, xcorr+i, len) |
|
||||
@ maxcorr = max(maxcorr, sum0, sum1, sum2, sum3) |
|
||||
CMP r0, r6 |
|
||||
@ _y+=4 |
|
||||
ADD r5, r5, #8 |
|
||||
MOVLT r0, r6 |
|
||||
CMP r0, r7 |
|
||||
MOVLT r0, r7 |
|
||||
CMP r0, r8 |
|
||||
MOVLT r0, r8 |
|
||||
CMP r0, r9 |
|
||||
MOVLT r0, r9 |
|
||||
STMIA r2!, {r6-r9} |
|
||||
SUBS r1, r1, #4 |
|
||||
BGE celt_pitch_xcorr_edsp_process4 |
|
||||
celt_pitch_xcorr_edsp_process2: |
|
||||
ADDS r1, r1, #2 |
|
||||
BLT celt_pitch_xcorr_edsp_process1a |
|
||||
SUBS r12, r3, #4 |
|
||||
@ {r10, r11} = {sum0, sum1} = {0, 0} |
|
||||
MOV r10, #0 |
|
||||
MOV r11, #0 |
|
||||
LDR r8, [r5], #4 |
|
||||
BLE celt_pitch_xcorr_edsp_process2_loop_done |
|
||||
LDR r6, [r4], #4 |
|
||||
LDR r9, [r5], #4 |
|
||||
celt_pitch_xcorr_edsp_process2_loop4: |
|
||||
SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDR r7, [r4], #4 |
|
||||
SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
SUBS r12, r12, #4 @ j-=4 |
|
||||
SMLATT r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_1, y_1) |
|
||||
LDR r8, [r5], #4 |
|
||||
SMLATB r11, r6, r9, r11 @ sum1 = MAC16_16(sum1, x_1, y_2) |
|
||||
LDRGT r6, [r4], #4 |
|
||||
SMLABB r10, r7, r9, r10 @ sum0 = MAC16_16(sum0, x_2, y_2) |
|
||||
SMLABT r11, r7, r9, r11 @ sum1 = MAC16_16(sum1, x_2, y_3) |
|
||||
SMLATT r10, r7, r9, r10 @ sum0 = MAC16_16(sum0, x_3, y_3) |
|
||||
LDRGT r9, [r5], #4 |
|
||||
SMLATB r11, r7, r8, r11 @ sum1 = MAC16_16(sum1, x_3, y_4) |
|
||||
BGT celt_pitch_xcorr_edsp_process2_loop4 |
|
||||
celt_pitch_xcorr_edsp_process2_loop_done: |
|
||||
ADDS r12, r12, #2 |
|
||||
BLE celt_pitch_xcorr_edsp_process2_1 |
|
||||
LDR r6, [r4], #4 |
|
||||
@ Stall |
|
||||
SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDR r9, [r5], #4 |
|
||||
SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
SUB r12, r12, #2 |
|
||||
SMLATT r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_1, y_1) |
|
||||
MOV r8, r9 |
|
||||
SMLATB r11, r6, r9, r11 @ sum1 = MAC16_16(sum1, x_1, y_2) |
|
||||
celt_pitch_xcorr_edsp_process2_1: |
|
||||
LDRH r6, [r4], #2 |
|
||||
ADDS r12, r12, #1 |
|
||||
@ Stall |
|
||||
SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDRHGT r7, [r4], #2 |
|
||||
SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
BLE celt_pitch_xcorr_edsp_process2_done |
|
||||
LDRH r9, [r5], #2 |
|
||||
SMLABT r10, r7, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_1) |
|
||||
SMLABB r11, r7, r9, r11 @ sum1 = MAC16_16(sum1, x_0, y_2) |
|
||||
celt_pitch_xcorr_edsp_process2_done: |
|
||||
@ Restore _x |
|
||||
SUB r4, r4, r3, LSL #1 |
|
||||
@ Restore and advance _y |
|
||||
SUB r5, r5, r3, LSL #1 |
|
||||
@ maxcorr = max(maxcorr, sum0) |
|
||||
CMP r0, r10 |
|
||||
ADD r5, r5, #2 |
|
||||
MOVLT r0, r10 |
|
||||
SUB r1, r1, #2 |
|
||||
@ maxcorr = max(maxcorr, sum1) |
|
||||
CMP r0, r11 |
|
||||
@ xcorr[i] = sum |
|
||||
STR r10, [r2], #4 |
|
||||
MOVLT r0, r11 |
|
||||
STR r11, [r2], #4 |
|
||||
celt_pitch_xcorr_edsp_process1a: |
|
||||
ADDS r1, r1, #1 |
|
||||
BLT celt_pitch_xcorr_edsp_done |
|
||||
SUBS r12, r3, #4 |
|
||||
@ r14 = sum = 0 |
|
||||
MOV r14, #0 |
|
||||
BLT celt_pitch_xcorr_edsp_process1a_loop_done |
|
||||
LDR r6, [r4], #4 |
|
||||
LDR r8, [r5], #4 |
|
||||
LDR r7, [r4], #4 |
|
||||
LDR r9, [r5], #4 |
|
||||
celt_pitch_xcorr_edsp_process1a_loop4: |
|
||||
SMLABB r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) |
|
||||
SUBS r12, r12, #4 @ j-=4 |
|
||||
SMLATT r14, r6, r8, r14 @ sum = MAC16_16(sum, x_1, y_1) |
|
||||
LDRGE r6, [r4], #4 |
|
||||
SMLABB r14, r7, r9, r14 @ sum = MAC16_16(sum, x_2, y_2) |
|
||||
LDRGE r8, [r5], #4 |
|
||||
SMLATT r14, r7, r9, r14 @ sum = MAC16_16(sum, x_3, y_3) |
|
||||
LDRGE r7, [r4], #4 |
|
||||
LDRGE r9, [r5], #4 |
|
||||
BGE celt_pitch_xcorr_edsp_process1a_loop4 |
|
||||
celt_pitch_xcorr_edsp_process1a_loop_done: |
|
||||
ADDS r12, r12, #2 |
|
||||
LDRGE r6, [r4], #4 |
|
||||
LDRGE r8, [r5], #4 |
|
||||
@ Stall |
|
||||
SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) |
|
||||
SUBGE r12, r12, #2 |
|
||||
SMLATTGE r14, r6, r8, r14 @ sum = MAC16_16(sum, x_1, y_1) |
|
||||
ADDS r12, r12, #1 |
|
||||
LDRHGE r6, [r4], #2 |
|
||||
LDRHGE r8, [r5], #2 |
|
||||
@ Stall |
|
||||
SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, *x, *y) |
|
||||
@ maxcorr = max(maxcorr, sum) |
|
||||
CMP r0, r14 |
|
||||
@ xcorr[i] = sum |
|
||||
STR r14, [r2], #4 |
|
||||
MOVLT r0, r14 |
|
||||
celt_pitch_xcorr_edsp_done: |
|
||||
LDMFD sp!, {r4-r11, pc} |
|
||||
.size celt_pitch_xcorr_edsp, .-celt_pitch_xcorr_edsp @ ENDP |
|
||||
|
|
||||
.endif |
|
||||
|
|
||||
@ END: |
|
||||
.section .note.GNU-stack,"",%progbits |
|
||||
@ -1,551 +0,0 @@ |
|||||
; Copyright (c) 2007-2008 CSIRO |
|
||||
; Copyright (c) 2007-2009 Xiph.Org Foundation |
|
||||
; Copyright (c) 2013 Parrot |
|
||||
; Written by Aurélien Zanelli |
|
||||
; |
|
||||
; 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. |
|
||||
|
|
||||
AREA |.text|, CODE, READONLY |
|
||||
|
|
||||
GET celt/arm/armopts.s |
|
||||
|
|
||||
IF OPUS_ARM_MAY_HAVE_EDSP |
|
||||
EXPORT celt_pitch_xcorr_edsp |
|
||||
ENDIF |
|
||||
|
|
||||
IF OPUS_ARM_MAY_HAVE_NEON |
|
||||
EXPORT celt_pitch_xcorr_neon |
|
||||
ENDIF |
|
||||
|
|
||||
IF OPUS_ARM_MAY_HAVE_NEON |
|
||||
|
|
||||
; Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3 |
|
||||
xcorr_kernel_neon PROC |
|
||||
xcorr_kernel_neon_start |
|
||||
; input: |
|
||||
; r3 = int len |
|
||||
; r4 = opus_val16 *x |
|
||||
; r5 = opus_val16 *y |
|
||||
; q0 = opus_val32 sum[4] |
|
||||
; output: |
|
||||
; q0 = opus_val32 sum[4] |
|
||||
; preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15 |
|
||||
; internal usage: |
|
||||
; r12 = int j |
|
||||
; d3 = y_3|y_2|y_1|y_0 |
|
||||
; q2 = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4 |
|
||||
; q3 = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0 |
|
||||
; q8 = scratch |
|
||||
; |
|
||||
; Load y[0...3] |
|
||||
; This requires len>0 to always be valid (which we assert in the C code). |
|
||||
VLD1.16 {d5}, [r5]! |
|
||||
SUBS r12, r3, #8 |
|
||||
BLE xcorr_kernel_neon_process4 |
|
||||
; Process 8 samples at a time. |
|
||||
; This loop loads one y value more than we actually need. Therefore we have to |
|
||||
; stop as soon as there are 8 or fewer samples left (instead of 7), to avoid |
|
||||
; reading past the end of the array. |
|
||||
xcorr_kernel_neon_process8 |
|
||||
; This loop has 19 total instructions (10 cycles to issue, minimum), with |
|
||||
; - 2 cycles of ARM insrtuctions, |
|
||||
; - 10 cycles of load/store/byte permute instructions, and |
|
||||
; - 9 cycles of data processing instructions. |
|
||||
; On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the |
|
||||
; latter two categories, meaning the whole loop should run in 10 cycles per |
|
||||
; iteration, barring cache misses. |
|
||||
; |
|
||||
; Load x[0...7] |
|
||||
VLD1.16 {d6, d7}, [r4]! |
|
||||
; Unlike VMOV, VAND is a data processsing instruction (and doesn't get |
|
||||
; assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. |
|
||||
VAND d3, d5, d5 |
|
||||
SUBS r12, r12, #8 |
|
||||
; Load y[4...11] |
|
||||
VLD1.16 {d4, d5}, [r5]! |
|
||||
VMLAL.S16 q0, d3, d6[0] |
|
||||
VEXT.16 d16, d3, d4, #1 |
|
||||
VMLAL.S16 q0, d4, d7[0] |
|
||||
VEXT.16 d17, d4, d5, #1 |
|
||||
VMLAL.S16 q0, d16, d6[1] |
|
||||
VEXT.16 d16, d3, d4, #2 |
|
||||
VMLAL.S16 q0, d17, d7[1] |
|
||||
VEXT.16 d17, d4, d5, #2 |
|
||||
VMLAL.S16 q0, d16, d6[2] |
|
||||
VEXT.16 d16, d3, d4, #3 |
|
||||
VMLAL.S16 q0, d17, d7[2] |
|
||||
VEXT.16 d17, d4, d5, #3 |
|
||||
VMLAL.S16 q0, d16, d6[3] |
|
||||
VMLAL.S16 q0, d17, d7[3] |
|
||||
BGT xcorr_kernel_neon_process8 |
|
||||
; Process 4 samples here if we have > 4 left (still reading one extra y value). |
|
||||
xcorr_kernel_neon_process4 |
|
||||
ADDS r12, r12, #4 |
|
||||
BLE xcorr_kernel_neon_process2 |
|
||||
; Load x[0...3] |
|
||||
VLD1.16 d6, [r4]! |
|
||||
; Use VAND since it's a data processing instruction again. |
|
||||
VAND d4, d5, d5 |
|
||||
SUB r12, r12, #4 |
|
||||
; Load y[4...7] |
|
||||
VLD1.16 d5, [r5]! |
|
||||
VMLAL.S16 q0, d4, d6[0] |
|
||||
VEXT.16 d16, d4, d5, #1 |
|
||||
VMLAL.S16 q0, d16, d6[1] |
|
||||
VEXT.16 d16, d4, d5, #2 |
|
||||
VMLAL.S16 q0, d16, d6[2] |
|
||||
VEXT.16 d16, d4, d5, #3 |
|
||||
VMLAL.S16 q0, d16, d6[3] |
|
||||
; Process 2 samples here if we have > 2 left (still reading one extra y value). |
|
||||
xcorr_kernel_neon_process2 |
|
||||
ADDS r12, r12, #2 |
|
||||
BLE xcorr_kernel_neon_process1 |
|
||||
; Load x[0...1] |
|
||||
VLD2.16 {d6[],d7[]}, [r4]! |
|
||||
; Use VAND since it's a data processing instruction again. |
|
||||
VAND d4, d5, d5 |
|
||||
SUB r12, r12, #2 |
|
||||
; Load y[4...5] |
|
||||
VLD1.32 {d5[]}, [r5]! |
|
||||
VMLAL.S16 q0, d4, d6 |
|
||||
VEXT.16 d16, d4, d5, #1 |
|
||||
; Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI |
|
||||
; instead of VEXT, since it's a data-processing instruction. |
|
||||
VSRI.64 d5, d4, #32 |
|
||||
VMLAL.S16 q0, d16, d7 |
|
||||
; Process 1 sample using the extra y value we loaded above. |
|
||||
xcorr_kernel_neon_process1 |
|
||||
; Load next *x |
|
||||
VLD1.16 {d6[]}, [r4]! |
|
||||
ADDS r12, r12, #1 |
|
||||
; y[0...3] are left in d5 from prior iteration(s) (if any) |
|
||||
VMLAL.S16 q0, d5, d6 |
|
||||
MOVLE pc, lr |
|
||||
; Now process 1 last sample, not reading ahead. |
|
||||
; Load last *y |
|
||||
VLD1.16 {d4[]}, [r5]! |
|
||||
VSRI.64 d4, d5, #16 |
|
||||
; Load last *x |
|
||||
VLD1.16 {d6[]}, [r4]! |
|
||||
VMLAL.S16 q0, d4, d6 |
|
||||
MOV pc, lr |
|
||||
ENDP |
|
||||
|
|
||||
; opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y, |
|
||||
; opus_val32 *xcorr, int len, int max_pitch, int arch) |
|
||||
celt_pitch_xcorr_neon PROC |
|
||||
; input: |
|
||||
; r0 = opus_val16 *_x |
|
||||
; r1 = opus_val16 *_y |
|
||||
; r2 = opus_val32 *xcorr |
|
||||
; r3 = int len |
|
||||
; output: |
|
||||
; r0 = int maxcorr |
|
||||
; internal usage: |
|
||||
; r4 = opus_val16 *x (for xcorr_kernel_neon()) |
|
||||
; r5 = opus_val16 *y (for xcorr_kernel_neon()) |
|
||||
; r6 = int max_pitch |
|
||||
; r12 = int j |
|
||||
; q15 = int maxcorr[4] (q15 is not used by xcorr_kernel_neon()) |
|
||||
; ignored: |
|
||||
; int arch |
|
||||
STMFD sp!, {r4-r6, lr} |
|
||||
LDR r6, [sp, #16] |
|
||||
VMOV.S32 q15, #1 |
|
||||
; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done |
|
||||
SUBS r6, r6, #4 |
|
||||
BLT celt_pitch_xcorr_neon_process4_done |
|
||||
celt_pitch_xcorr_neon_process4 |
|
||||
; xcorr_kernel_neon parameters: |
|
||||
; r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0} |
|
||||
MOV r4, r0 |
|
||||
MOV r5, r1 |
|
||||
VEOR q0, q0, q0 |
|
||||
; xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3. |
|
||||
; So we don't save/restore any other registers. |
|
||||
BL xcorr_kernel_neon_start |
|
||||
SUBS r6, r6, #4 |
|
||||
VST1.32 {q0}, [r2]! |
|
||||
; _y += 4 |
|
||||
ADD r1, r1, #8 |
|
||||
VMAX.S32 q15, q15, q0 |
|
||||
; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done |
|
||||
BGE celt_pitch_xcorr_neon_process4 |
|
||||
; We have less than 4 sums left to compute. |
|
||||
celt_pitch_xcorr_neon_process4_done |
|
||||
ADDS r6, r6, #4 |
|
||||
; Reduce maxcorr to a single value |
|
||||
VMAX.S32 d30, d30, d31 |
|
||||
VPMAX.S32 d30, d30, d30 |
|
||||
; if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done |
|
||||
BLE celt_pitch_xcorr_neon_done |
|
||||
; Now compute each remaining sum one at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining |
|
||||
MOV r4, r0 |
|
||||
MOV r5, r1 |
|
||||
VMOV.I32 q0, #0 |
|
||||
SUBS r12, r3, #8 |
|
||||
BLT celt_pitch_xcorr_neon_process_remaining4 |
|
||||
; Sum terms 8 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop8 |
|
||||
; Load x[0...7] |
|
||||
VLD1.16 {q1}, [r4]! |
|
||||
; Load y[0...7] |
|
||||
VLD1.16 {q2}, [r5]! |
|
||||
SUBS r12, r12, #8 |
|
||||
VMLAL.S16 q0, d4, d2 |
|
||||
VMLAL.S16 q0, d5, d3 |
|
||||
BGE celt_pitch_xcorr_neon_process_remaining_loop8 |
|
||||
; Sum terms 4 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining4 |
|
||||
ADDS r12, r12, #4 |
|
||||
BLT celt_pitch_xcorr_neon_process_remaining4_done |
|
||||
; Load x[0...3] |
|
||||
VLD1.16 {d2}, [r4]! |
|
||||
; Load y[0...3] |
|
||||
VLD1.16 {d3}, [r5]! |
|
||||
SUB r12, r12, #4 |
|
||||
VMLAL.S16 q0, d3, d2 |
|
||||
celt_pitch_xcorr_neon_process_remaining4_done |
|
||||
; Reduce the sum to a single value. |
|
||||
VADD.S32 d0, d0, d1 |
|
||||
VPADDL.S32 d0, d0 |
|
||||
ADDS r12, r12, #4 |
|
||||
BLE celt_pitch_xcorr_neon_process_remaining_loop_done |
|
||||
; Sum terms 1 at a time. |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop1 |
|
||||
VLD1.16 {d2[]}, [r4]! |
|
||||
VLD1.16 {d3[]}, [r5]! |
|
||||
SUBS r12, r12, #1 |
|
||||
VMLAL.S16 q0, d2, d3 |
|
||||
BGT celt_pitch_xcorr_neon_process_remaining_loop1 |
|
||||
celt_pitch_xcorr_neon_process_remaining_loop_done |
|
||||
VST1.32 {d0[0]}, [r2]! |
|
||||
VMAX.S32 d30, d30, d0 |
|
||||
SUBS r6, r6, #1 |
|
||||
; _y++ |
|
||||
ADD r1, r1, #2 |
|
||||
; if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining |
|
||||
BGT celt_pitch_xcorr_neon_process_remaining |
|
||||
celt_pitch_xcorr_neon_done |
|
||||
VMOV.32 r0, d30[0] |
|
||||
LDMFD sp!, {r4-r6, pc} |
|
||||
ENDP |
|
||||
|
|
||||
ENDIF |
|
||||
|
|
||||
IF OPUS_ARM_MAY_HAVE_EDSP |
|
||||
|
|
||||
; This will get used on ARMv7 devices without NEON, so it has been optimized |
|
||||
; to take advantage of dual-issuing where possible. |
|
||||
xcorr_kernel_edsp PROC |
|
||||
xcorr_kernel_edsp_start |
|
||||
; input: |
|
||||
; r3 = int len |
|
||||
; r4 = opus_val16 *_x (must be 32-bit aligned) |
|
||||
; r5 = opus_val16 *_y (must be 32-bit aligned) |
|
||||
; r6...r9 = opus_val32 sum[4] |
|
||||
; output: |
|
||||
; r6...r9 = opus_val32 sum[4] |
|
||||
; preserved: r0-r5 |
|
||||
; internal usage |
|
||||
; r2 = int j |
|
||||
; r12,r14 = opus_val16 x[4] |
|
||||
; r10,r11 = opus_val16 y[4] |
|
||||
STMFD sp!, {r2,r4,r5,lr} |
|
||||
LDR r10, [r5], #4 ; Load y[0...1] |
|
||||
SUBS r2, r3, #4 ; j = len-4 |
|
||||
LDR r11, [r5], #4 ; Load y[2...3] |
|
||||
BLE xcorr_kernel_edsp_process4_done |
|
||||
LDR r12, [r4], #4 ; Load x[0...1] |
|
||||
; Stall |
|
||||
xcorr_kernel_edsp_process4 |
|
||||
; The multiplies must issue from pipeline 0, and can't dual-issue with each |
|
||||
; other. Every other instruction here dual-issues with a multiply, and is |
|
||||
; thus "free". There should be no stalls in the body of the loop. |
|
||||
SMLABB r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x_0,y_0) |
|
||||
LDR r14, [r4], #4 ; Load x[2...3] |
|
||||
SMLABT r7, r12, r10, r7 ; sum[1] = MAC16_16(sum[1],x_0,y_1) |
|
||||
SUBS r2, r2, #4 ; j-=4 |
|
||||
SMLABB r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x_0,y_2) |
|
||||
SMLABT r9, r12, r11, r9 ; sum[3] = MAC16_16(sum[3],x_0,y_3) |
|
||||
SMLATT r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x_1,y_1) |
|
||||
LDR r10, [r5], #4 ; Load y[4...5] |
|
||||
SMLATB r7, r12, r11, r7 ; sum[1] = MAC16_16(sum[1],x_1,y_2) |
|
||||
SMLATT r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x_1,y_3) |
|
||||
SMLATB r9, r12, r10, r9 ; sum[3] = MAC16_16(sum[3],x_1,y_4) |
|
||||
LDRGT r12, [r4], #4 ; Load x[0...1] |
|
||||
SMLABB r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],x_2,y_2) |
|
||||
SMLABT r7, r14, r11, r7 ; sum[1] = MAC16_16(sum[1],x_2,y_3) |
|
||||
SMLABB r8, r14, r10, r8 ; sum[2] = MAC16_16(sum[2],x_2,y_4) |
|
||||
SMLABT r9, r14, r10, r9 ; sum[3] = MAC16_16(sum[3],x_2,y_5) |
|
||||
SMLATT r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],x_3,y_3) |
|
||||
LDR r11, [r5], #4 ; Load y[6...7] |
|
||||
SMLATB r7, r14, r10, r7 ; sum[1] = MAC16_16(sum[1],x_3,y_4) |
|
||||
SMLATT r8, r14, r10, r8 ; sum[2] = MAC16_16(sum[2],x_3,y_5) |
|
||||
SMLATB r9, r14, r11, r9 ; sum[3] = MAC16_16(sum[3],x_3,y_6) |
|
||||
BGT xcorr_kernel_edsp_process4 |
|
||||
xcorr_kernel_edsp_process4_done |
|
||||
ADDS r2, r2, #4 |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
LDRH r12, [r4], #2 ; r12 = *x++ |
|
||||
SUBS r2, r2, #1 ; j-- |
|
||||
; Stall |
|
||||
SMLABB r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x,y_0) |
|
||||
LDRHGT r14, [r4], #2 ; r14 = *x++ |
|
||||
SMLABT r7, r12, r10, r7 ; sum[1] = MAC16_16(sum[1],x,y_1) |
|
||||
SMLABB r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x,y_2) |
|
||||
SMLABT r9, r12, r11, r9 ; sum[3] = MAC16_16(sum[3],x,y_3) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABT r6, r14, r10, r6 ; sum[0] = MAC16_16(sum[0],x,y_1) |
|
||||
SUBS r2, r2, #1 ; j-- |
|
||||
SMLABB r7, r14, r11, r7 ; sum[1] = MAC16_16(sum[1],x,y_2) |
|
||||
LDRH r10, [r5], #2 ; r10 = y_4 = *y++ |
|
||||
SMLABT r8, r14, r11, r8 ; sum[2] = MAC16_16(sum[2],x,y_3) |
|
||||
LDRHGT r12, [r4], #2 ; r12 = *x++ |
|
||||
SMLABB r9, r14, r10, r9 ; sum[3] = MAC16_16(sum[3],x,y_4) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABB r6, r12, r11, r6 ; sum[0] = MAC16_16(sum[0],tmp,y_2) |
|
||||
CMP r2, #1 ; j-- |
|
||||
SMLABT r7, r12, r11, r7 ; sum[1] = MAC16_16(sum[1],tmp,y_3) |
|
||||
LDRH r2, [r5], #2 ; r2 = y_5 = *y++ |
|
||||
SMLABB r8, r12, r10, r8 ; sum[2] = MAC16_16(sum[2],tmp,y_4) |
|
||||
LDRHGT r14, [r4] ; r14 = *x |
|
||||
SMLABB r9, r12, r2, r9 ; sum[3] = MAC16_16(sum[3],tmp,y_5) |
|
||||
BLE xcorr_kernel_edsp_done |
|
||||
SMLABT r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],tmp,y_3) |
|
||||
LDRH r11, [r5] ; r11 = y_6 = *y |
|
||||
SMLABB r7, r14, r10, r7 ; sum[1] = MAC16_16(sum[1],tmp,y_4) |
|
||||
SMLABB r8, r14, r2, r8 ; sum[2] = MAC16_16(sum[2],tmp,y_5) |
|
||||
SMLABB r9, r14, r11, r9 ; sum[3] = MAC16_16(sum[3],tmp,y_6) |
|
||||
xcorr_kernel_edsp_done |
|
||||
LDMFD sp!, {r2,r4,r5,pc} |
|
||||
ENDP |
|
||||
|
|
||||
celt_pitch_xcorr_edsp PROC |
|
||||
; input: |
|
||||
; r0 = opus_val16 *_x (must be 32-bit aligned) |
|
||||
; r1 = opus_val16 *_y (only needs to be 16-bit aligned) |
|
||||
; r2 = opus_val32 *xcorr |
|
||||
; r3 = int len |
|
||||
; output: |
|
||||
; r0 = maxcorr |
|
||||
; internal usage |
|
||||
; r4 = opus_val16 *x |
|
||||
; r5 = opus_val16 *y |
|
||||
; r6 = opus_val32 sum0 |
|
||||
; r7 = opus_val32 sum1 |
|
||||
; r8 = opus_val32 sum2 |
|
||||
; r9 = opus_val32 sum3 |
|
||||
; r1 = int max_pitch |
|
||||
; r12 = int j |
|
||||
; ignored: |
|
||||
; int arch |
|
||||
STMFD sp!, {r4-r11, lr} |
|
||||
MOV r5, r1 |
|
||||
LDR r1, [sp, #36] |
|
||||
MOV r4, r0 |
|
||||
TST r5, #3 |
|
||||
; maxcorr = 1 |
|
||||
MOV r0, #1 |
|
||||
BEQ celt_pitch_xcorr_edsp_process1u_done |
|
||||
; Compute one sum at the start to make y 32-bit aligned. |
|
||||
SUBS r12, r3, #4 |
|
||||
; r14 = sum = 0 |
|
||||
MOV r14, #0 |
|
||||
LDRH r8, [r5], #2 |
|
||||
BLE celt_pitch_xcorr_edsp_process1u_loop4_done |
|
||||
LDR r6, [r4], #4 |
|
||||
MOV r8, r8, LSL #16 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop4 |
|
||||
LDR r9, [r5], #4 |
|
||||
SMLABT r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) |
|
||||
LDR r7, [r4], #4 |
|
||||
SMLATB r14, r6, r9, r14 ; sum = MAC16_16(sum, x_1, y_1) |
|
||||
LDR r8, [r5], #4 |
|
||||
SMLABT r14, r7, r9, r14 ; sum = MAC16_16(sum, x_2, y_2) |
|
||||
SUBS r12, r12, #4 ; j-=4 |
|
||||
SMLATB r14, r7, r8, r14 ; sum = MAC16_16(sum, x_3, y_3) |
|
||||
LDRGT r6, [r4], #4 |
|
||||
BGT celt_pitch_xcorr_edsp_process1u_loop4 |
|
||||
MOV r8, r8, LSR #16 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop4_done |
|
||||
ADDS r12, r12, #4 |
|
||||
celt_pitch_xcorr_edsp_process1u_loop1 |
|
||||
LDRHGE r6, [r4], #2 |
|
||||
; Stall |
|
||||
SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, *x, *y) |
|
||||
SUBSGE r12, r12, #1 |
|
||||
LDRHGT r8, [r5], #2 |
|
||||
BGT celt_pitch_xcorr_edsp_process1u_loop1 |
|
||||
; Restore _x |
|
||||
SUB r4, r4, r3, LSL #1 |
|
||||
; Restore and advance _y |
|
||||
SUB r5, r5, r3, LSL #1 |
|
||||
; maxcorr = max(maxcorr, sum) |
|
||||
CMP r0, r14 |
|
||||
ADD r5, r5, #2 |
|
||||
MOVLT r0, r14 |
|
||||
SUBS r1, r1, #1 |
|
||||
; xcorr[i] = sum |
|
||||
STR r14, [r2], #4 |
|
||||
BLE celt_pitch_xcorr_edsp_done |
|
||||
celt_pitch_xcorr_edsp_process1u_done |
|
||||
; if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process2 |
|
||||
SUBS r1, r1, #4 |
|
||||
BLT celt_pitch_xcorr_edsp_process2 |
|
||||
celt_pitch_xcorr_edsp_process4 |
|
||||
; xcorr_kernel_edsp parameters: |
|
||||
; r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0} |
|
||||
MOV r6, #0 |
|
||||
MOV r7, #0 |
|
||||
MOV r8, #0 |
|
||||
MOV r9, #0 |
|
||||
BL xcorr_kernel_edsp_start ; xcorr_kernel_edsp(_x, _y+i, xcorr+i, len) |
|
||||
; maxcorr = max(maxcorr, sum0, sum1, sum2, sum3) |
|
||||
CMP r0, r6 |
|
||||
; _y+=4 |
|
||||
ADD r5, r5, #8 |
|
||||
MOVLT r0, r6 |
|
||||
CMP r0, r7 |
|
||||
MOVLT r0, r7 |
|
||||
CMP r0, r8 |
|
||||
MOVLT r0, r8 |
|
||||
CMP r0, r9 |
|
||||
MOVLT r0, r9 |
|
||||
STMIA r2!, {r6-r9} |
|
||||
SUBS r1, r1, #4 |
|
||||
BGE celt_pitch_xcorr_edsp_process4 |
|
||||
celt_pitch_xcorr_edsp_process2 |
|
||||
ADDS r1, r1, #2 |
|
||||
BLT celt_pitch_xcorr_edsp_process1a |
|
||||
SUBS r12, r3, #4 |
|
||||
; {r10, r11} = {sum0, sum1} = {0, 0} |
|
||||
MOV r10, #0 |
|
||||
MOV r11, #0 |
|
||||
LDR r8, [r5], #4 |
|
||||
BLE celt_pitch_xcorr_edsp_process2_loop_done |
|
||||
LDR r6, [r4], #4 |
|
||||
LDR r9, [r5], #4 |
|
||||
celt_pitch_xcorr_edsp_process2_loop4 |
|
||||
SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDR r7, [r4], #4 |
|
||||
SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
SUBS r12, r12, #4 ; j-=4 |
|
||||
SMLATT r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_1, y_1) |
|
||||
LDR r8, [r5], #4 |
|
||||
SMLATB r11, r6, r9, r11 ; sum1 = MAC16_16(sum1, x_1, y_2) |
|
||||
LDRGT r6, [r4], #4 |
|
||||
SMLABB r10, r7, r9, r10 ; sum0 = MAC16_16(sum0, x_2, y_2) |
|
||||
SMLABT r11, r7, r9, r11 ; sum1 = MAC16_16(sum1, x_2, y_3) |
|
||||
SMLATT r10, r7, r9, r10 ; sum0 = MAC16_16(sum0, x_3, y_3) |
|
||||
LDRGT r9, [r5], #4 |
|
||||
SMLATB r11, r7, r8, r11 ; sum1 = MAC16_16(sum1, x_3, y_4) |
|
||||
BGT celt_pitch_xcorr_edsp_process2_loop4 |
|
||||
celt_pitch_xcorr_edsp_process2_loop_done |
|
||||
ADDS r12, r12, #2 |
|
||||
BLE celt_pitch_xcorr_edsp_process2_1 |
|
||||
LDR r6, [r4], #4 |
|
||||
; Stall |
|
||||
SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDR r9, [r5], #4 |
|
||||
SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
SUB r12, r12, #2 |
|
||||
SMLATT r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_1, y_1) |
|
||||
MOV r8, r9 |
|
||||
SMLATB r11, r6, r9, r11 ; sum1 = MAC16_16(sum1, x_1, y_2) |
|
||||
celt_pitch_xcorr_edsp_process2_1 |
|
||||
LDRH r6, [r4], #2 |
|
||||
ADDS r12, r12, #1 |
|
||||
; Stall |
|
||||
SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) |
|
||||
LDRHGT r7, [r4], #2 |
|
||||
SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) |
|
||||
BLE celt_pitch_xcorr_edsp_process2_done |
|
||||
LDRH r9, [r5], #2 |
|
||||
SMLABT r10, r7, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_1) |
|
||||
SMLABB r11, r7, r9, r11 ; sum1 = MAC16_16(sum1, x_0, y_2) |
|
||||
celt_pitch_xcorr_edsp_process2_done |
|
||||
; Restore _x |
|
||||
SUB r4, r4, r3, LSL #1 |
|
||||
; Restore and advance _y |
|
||||
SUB r5, r5, r3, LSL #1 |
|
||||
; maxcorr = max(maxcorr, sum0) |
|
||||
CMP r0, r10 |
|
||||
ADD r5, r5, #2 |
|
||||
MOVLT r0, r10 |
|
||||
SUB r1, r1, #2 |
|
||||
; maxcorr = max(maxcorr, sum1) |
|
||||
CMP r0, r11 |
|
||||
; xcorr[i] = sum |
|
||||
STR r10, [r2], #4 |
|
||||
MOVLT r0, r11 |
|
||||
STR r11, [r2], #4 |
|
||||
celt_pitch_xcorr_edsp_process1a |
|
||||
ADDS r1, r1, #1 |
|
||||
BLT celt_pitch_xcorr_edsp_done |
|
||||
SUBS r12, r3, #4 |
|
||||
; r14 = sum = 0 |
|
||||
MOV r14, #0 |
|
||||
BLT celt_pitch_xcorr_edsp_process1a_loop_done |
|
||||
LDR r6, [r4], #4 |
|
||||
LDR r8, [r5], #4 |
|
||||
LDR r7, [r4], #4 |
|
||||
LDR r9, [r5], #4 |
|
||||
celt_pitch_xcorr_edsp_process1a_loop4 |
|
||||
SMLABB r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) |
|
||||
SUBS r12, r12, #4 ; j-=4 |
|
||||
SMLATT r14, r6, r8, r14 ; sum = MAC16_16(sum, x_1, y_1) |
|
||||
LDRGE r6, [r4], #4 |
|
||||
SMLABB r14, r7, r9, r14 ; sum = MAC16_16(sum, x_2, y_2) |
|
||||
LDRGE r8, [r5], #4 |
|
||||
SMLATT r14, r7, r9, r14 ; sum = MAC16_16(sum, x_3, y_3) |
|
||||
LDRGE r7, [r4], #4 |
|
||||
LDRGE r9, [r5], #4 |
|
||||
BGE celt_pitch_xcorr_edsp_process1a_loop4 |
|
||||
celt_pitch_xcorr_edsp_process1a_loop_done |
|
||||
ADDS r12, r12, #2 |
|
||||
LDRGE r6, [r4], #4 |
|
||||
LDRGE r8, [r5], #4 |
|
||||
; Stall |
|
||||
SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) |
|
||||
SUBGE r12, r12, #2 |
|
||||
SMLATTGE r14, r6, r8, r14 ; sum = MAC16_16(sum, x_1, y_1) |
|
||||
ADDS r12, r12, #1 |
|
||||
LDRHGE r6, [r4], #2 |
|
||||
LDRHGE r8, [r5], #2 |
|
||||
; Stall |
|
||||
SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, *x, *y) |
|
||||
; maxcorr = max(maxcorr, sum) |
|
||||
CMP r0, r14 |
|
||||
; xcorr[i] = sum |
|
||||
STR r14, [r2], #4 |
|
||||
MOVLT r0, r14 |
|
||||
celt_pitch_xcorr_edsp_done |
|
||||
LDMFD sp!, {r4-r11, pc} |
|
||||
ENDP |
|
||||
|
|
||||
ENDIF |
|
||||
|
|
||||
END |
|
||||
@ -1,71 +0,0 @@ |
|||||
/* Copyright (c) 2015 Xiph.Org Foundation
|
|
||||
Written by Viswanath Puttagunta */ |
|
||||
/**
|
|
||||
@file fft_arm.h |
|
||||
@brief ARM Neon Intrinsic optimizations for fft using NE10 library |
|
||||
*/ |
|
||||
|
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
|
|
||||
#if !defined(FFT_ARM_H) |
|
||||
#define FFT_ARM_H |
|
||||
|
|
||||
#include "kiss_fft.h" |
|
||||
|
|
||||
#if defined(HAVE_ARM_NE10) |
|
||||
|
|
||||
int opus_fft_alloc_arm_neon(kiss_fft_state *st); |
|
||||
void opus_fft_free_arm_neon(kiss_fft_state *st); |
|
||||
|
|
||||
void opus_fft_neon(const kiss_fft_state *st, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout); |
|
||||
|
|
||||
void opus_ifft_neon(const kiss_fft_state *st, |
|
||||
const kiss_fft_cpx *fin, |
|
||||
kiss_fft_cpx *fout); |
|
||||
|
|
||||
#if !defined(OPUS_HAVE_RTCD) |
|
||||
#define OVERRIDE_OPUS_FFT (1) |
|
||||
|
|
||||
#define opus_fft_alloc_arch(_st, arch) \ |
|
||||
((void)(arch), opus_fft_alloc_arm_neon(_st)) |
|
||||
|
|
||||
#define opus_fft_free_arch(_st, arch) \ |
|
||||
((void)(arch), opus_fft_free_arm_neon(_st)) |
|
||||
|
|
||||
#define opus_fft(_st, _fin, _fout, arch) \ |
|
||||
((void)(arch), opus_fft_neon(_st, _fin, _fout)) |
|
||||
|
|
||||
#define opus_ifft(_st, _fin, _fout, arch) \ |
|
||||
((void)(arch), opus_ifft_neon(_st, _fin, _fout)) |
|
||||
|
|
||||
#endif /* OPUS_HAVE_RTCD */ |
|
||||
|
|
||||
#endif /* HAVE_ARM_NE10 */ |
|
||||
|
|
||||
#endif |
|
||||
@ -1,35 +0,0 @@ |
|||||
/* Copyright (C) 2015 Vidyo */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef FIXED_ARM64_H |
|
||||
#define FIXED_ARM64_H |
|
||||
|
|
||||
#include <arm_neon.h> |
|
||||
|
|
||||
#undef SIG2WORD16 |
|
||||
#define SIG2WORD16(x) (vqmovns_s32(PSHR32((x), SIG_SHIFT))) |
|
||||
|
|
||||
#endif |
|
||||
@ -1,80 +0,0 @@ |
|||||
/* Copyright (C) 2013 Xiph.Org Foundation and contributors */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef FIXED_ARMv4_H |
|
||||
#define FIXED_ARMv4_H |
|
||||
|
|
||||
/** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ |
|
||||
#undef MULT16_32_Q16 |
|
||||
static OPUS_INLINE opus_val32 MULT16_32_Q16_armv4(opus_val16 a, opus_val32 b) |
|
||||
{ |
|
||||
unsigned rd_lo; |
|
||||
int rd_hi; |
|
||||
__asm__( |
|
||||
"#MULT16_32_Q16\n\t" |
|
||||
"smull %0, %1, %2, %3\n\t" |
|
||||
: "=&r"(rd_lo), "=&r"(rd_hi) |
|
||||
: "%r"(b),"r"(SHL32(a,16)) |
|
||||
); |
|
||||
return rd_hi; |
|
||||
} |
|
||||
#define MULT16_32_Q16(a, b) (MULT16_32_Q16_armv4(a, b)) |
|
||||
|
|
||||
|
|
||||
/** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ |
|
||||
#undef MULT16_32_Q15 |
|
||||
static OPUS_INLINE opus_val32 MULT16_32_Q15_armv4(opus_val16 a, opus_val32 b) |
|
||||
{ |
|
||||
unsigned rd_lo; |
|
||||
int rd_hi; |
|
||||
__asm__( |
|
||||
"#MULT16_32_Q15\n\t" |
|
||||
"smull %0, %1, %2, %3\n\t" |
|
||||
: "=&r"(rd_lo), "=&r"(rd_hi) |
|
||||
: "%r"(b), "r"(SHL32(a,16)) |
|
||||
); |
|
||||
/*We intentionally don't OR in the high bit of rd_lo for speed.*/ |
|
||||
return SHL32(rd_hi,1); |
|
||||
} |
|
||||
#define MULT16_32_Q15(a, b) (MULT16_32_Q15_armv4(a, b)) |
|
||||
|
|
||||
|
|
||||
/** 16x32 multiply, followed by a 15-bit shift right and 32-bit add.
|
|
||||
b must fit in 31 bits. |
|
||||
Result fits in 32 bits. */ |
|
||||
#undef MAC16_32_Q15 |
|
||||
#define MAC16_32_Q15(c, a, b) ADD32(c, MULT16_32_Q15(a, b)) |
|
||||
|
|
||||
/** 16x32 multiply, followed by a 16-bit shift right and 32-bit add.
|
|
||||
Result fits in 32 bits. */ |
|
||||
#undef MAC16_32_Q16 |
|
||||
#define MAC16_32_Q16(c, a, b) ADD32(c, MULT16_32_Q16(a, b)) |
|
||||
|
|
||||
/** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ |
|
||||
#undef MULT32_32_Q31 |
|
||||
#define MULT32_32_Q31(a,b) (opus_val32)((((opus_int64)(a)) * ((opus_int64)(b)))>>31) |
|
||||
|
|
||||
#endif |
|
||||
@ -1,151 +0,0 @@ |
|||||
/* Copyright (C) 2007-2009 Xiph.Org Foundation
|
|
||||
Copyright (C) 2003-2008 Jean-Marc Valin |
|
||||
Copyright (C) 2007-2008 CSIRO |
|
||||
Copyright (C) 2013 Parrot */ |
|
||||
/*
|
|
||||
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. |
|
||||
*/ |
|
||||
|
|
||||
#ifndef FIXED_ARMv5E_H |
|
||||
#define FIXED_ARMv5E_H |
|
||||
|
|
||||
#include "fixed_armv4.h" |
|
||||
|
|
||||
/** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ |
|
||||
#undef MULT16_32_Q16 |
|
||||
static OPUS_INLINE opus_val32 MULT16_32_Q16_armv5e(opus_val16 a, opus_val32 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MULT16_32_Q16\n\t" |
|
||||
"smulwb %0, %1, %2\n\t" |
|
||||
: "=r"(res) |
|
||||
: "r"(b),"r"(a) |
|
||||
); |
|
||||
return res; |
|
||||
} |
|
||||
#define MULT16_32_Q16(a, b) (MULT16_32_Q16_armv5e(a, b)) |
|
||||
|
|
||||
|
|
||||
/** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ |
|
||||
#undef MULT16_32_Q15 |
|
||||
static OPUS_INLINE opus_val32 MULT16_32_Q15_armv5e(opus_val16 a, opus_val32 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MULT16_32_Q15\n\t" |
|
||||
"smulwb %0, %1, %2\n\t" |
|
||||
: "=r"(res) |
|
||||
: "r"(b), "r"(a) |
|
||||
); |
|
||||
return SHL32(res,1); |
|
||||
} |
|
||||
#define MULT16_32_Q15(a, b) (MULT16_32_Q15_armv5e(a, b)) |
|
||||
|
|
||||
|
|
||||
/** 16x32 multiply, followed by a 15-bit shift right and 32-bit add.
|
|
||||
b must fit in 31 bits. |
|
||||
Result fits in 32 bits. */ |
|
||||
#undef MAC16_32_Q15 |
|
||||
static OPUS_INLINE opus_val32 MAC16_32_Q15_armv5e(opus_val32 c, opus_val16 a, |
|
||||
opus_val32 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MAC16_32_Q15\n\t" |
|
||||
"smlawb %0, %1, %2, %3;\n" |
|
||||
: "=r"(res) |
|
||||
: "r"(SHL32(b,1)), "r"(a), "r"(c) |
|
||||
); |
|
||||
return res; |
|
||||
} |
|
||||
#define MAC16_32_Q15(c, a, b) (MAC16_32_Q15_armv5e(c, a, b)) |
|
||||
|
|
||||
/** 16x32 multiply, followed by a 16-bit shift right and 32-bit add.
|
|
||||
Result fits in 32 bits. */ |
|
||||
#undef MAC16_32_Q16 |
|
||||
static OPUS_INLINE opus_val32 MAC16_32_Q16_armv5e(opus_val32 c, opus_val16 a, |
|
||||
opus_val32 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MAC16_32_Q16\n\t" |
|
||||
"smlawb %0, %1, %2, %3;\n" |
|
||||
: "=r"(res) |
|
||||
: "r"(b), "r"(a), "r"(c) |
|
||||
); |
|
||||
return res; |
|
||||
} |
|
||||
#define MAC16_32_Q16(c, a, b) (MAC16_32_Q16_armv5e(c, a, b)) |
|
||||
|
|
||||
/** 16x16 multiply-add where the result fits in 32 bits */ |
|
||||
#undef MAC16_16 |
|
||||
static OPUS_INLINE opus_val32 MAC16_16_armv5e(opus_val32 c, opus_val16 a, |
|
||||
opus_val16 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MAC16_16\n\t" |
|
||||
"smlabb %0, %1, %2, %3;\n" |
|
||||
: "=r"(res) |
|
||||
: "r"(a), "r"(b), "r"(c) |
|
||||
); |
|
||||
return res; |
|
||||
} |
|
||||
#define MAC16_16(c, a, b) (MAC16_16_armv5e(c, a, b)) |
|
||||
|
|
||||
/** 16x16 multiplication where the result fits in 32 bits */ |
|
||||
#undef MULT16_16 |
|
||||
static OPUS_INLINE opus_val32 MULT16_16_armv5e(opus_val16 a, opus_val16 b) |
|
||||
{ |
|
||||
int res; |
|
||||
__asm__( |
|
||||
"#MULT16_16\n\t" |
|
||||
"smulbb %0, %1, %2;\n" |
|
||||
: "=r"(res) |
|
||||
: "r"(a), "r"(b) |
|
||||
); |
|
||||
return res; |
|
||||
} |
|
||||
#define MULT16_16(a, b) (MULT16_16_armv5e(a, b)) |
|
||||
|
|
||||
#ifdef OPUS_ARM_INLINE_MEDIA |
|
||||
|
|
||||
#undef SIG2WORD16 |
|
||||
static OPUS_INLINE opus_val16 SIG2WORD16_armv6(opus_val32 x) |
|
||||
{ |
|
||||
celt_sig res; |
|
||||
__asm__( |
|
||||
"#SIG2WORD16\n\t" |
|
||||
"ssat %0, #16, %1, ASR #12\n\t" |
|
||||
: "=r"(res) |
|
||||
: "r"(x+2048) |
|
||||
); |
|
||||
return EXTRACT16(res); |
|
||||
} |
|
||||
#define SIG2WORD16(x) (SIG2WORD16_armv6(x)) |
|
||||
|
|
||||
#endif /* OPUS_ARM_INLINE_MEDIA */ |
|
||||
|
|
||||
#endif |
|
||||
@ -1,121 +0,0 @@ |
|||||
/*Copyright (c) 2013, Xiph.Org Foundation and contributors.
|
|
||||
|
|
||||
All rights reserved. |
|
||||
|
|
||||
Redistribution and use in source and binary forms, with or without |
|
||||
modification, are permitted provided that the following conditions are met: |
|
||||
|
|
||||
* Redistributions of source code must retain the above copyright notice, |
|
||||
this list of conditions and the following disclaimer. |
|
||||
* Redistributions in binary form must reproduce the above copyright notice, |
|
||||
this list of conditions and the following disclaimer in the |
|
||||
documentation and/or other materials provided with the distribution. |
|
||||
|
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
POSSIBILITY OF SUCH DAMAGE.*/ |
|
||||
|
|
||||
#ifndef KISS_FFT_ARMv4_H |
|
||||
#define KISS_FFT_ARMv4_H |
|
||||
|
|
||||
#if !defined(KISS_FFT_GUTS_H) |
|
||||
#error "This file should only be included from _kiss_fft_guts.h" |
|
||||
#endif |
|
||||
|
|
||||
#ifdef FIXED_POINT |
|
||||
|
|
||||
#undef C_MUL |
|
||||
#define C_MUL(m,a,b) \ |
|
||||
do{ \ |
|
||||
int br__; \ |
|
||||
int bi__; \ |
|
||||
int tt__; \ |
|
||||
__asm__ __volatile__( \ |
|
||||
"#C_MUL\n\t" \ |
|
||||
"ldrsh %[br], [%[bp], #0]\n\t" \ |
|
||||
"ldm %[ap], {r0,r1}\n\t" \ |
|
||||
"ldrsh %[bi], [%[bp], #2]\n\t" \ |
|
||||
"smull %[tt], %[mi], r1, %[br]\n\t" \ |
|
||||
"smlal %[tt], %[mi], r0, %[bi]\n\t" \ |
|
||||
"rsb %[bi], %[bi], #0\n\t" \ |
|
||||
"smull %[br], %[mr], r0, %[br]\n\t" \ |
|
||||
"mov %[tt], %[tt], lsr #15\n\t" \ |
|
||||
"smlal %[br], %[mr], r1, %[bi]\n\t" \ |
|
||||
"orr %[mi], %[tt], %[mi], lsl #17\n\t" \ |
|
||||
"mov %[br], %[br], lsr #15\n\t" \ |
|
||||
"orr %[mr], %[br], %[mr], lsl #17\n\t" \ |
|
||||
: [mr]"=r"((m).r), [mi]"=r"((m).i), \ |
|
||||
[br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ |
|
||||
: [ap]"r"(&(a)), [bp]"r"(&(b)) \ |
|
||||
: "r0", "r1" \ |
|
||||
); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#undef C_MUL4 |
|
||||
#define C_MUL4(m,a,b) \ |
|
||||
do{ \ |
|
||||
int br__; \ |
|
||||
int bi__; \ |
|
||||
int tt__; \ |
|
||||
__asm__ __volatile__( \ |
|
||||
"#C_MUL4\n\t" \ |
|
||||
"ldrsh %[br], [%[bp], #0]\n\t" \ |
|
||||
"ldm %[ap], {r0,r1}\n\t" \ |
|
||||
"ldrsh %[bi], [%[bp], #2]\n\t" \ |
|
||||
"smull %[tt], %[mi], r1, %[br]\n\t" \ |
|
||||
"smlal %[tt], %[mi], r0, %[bi]\n\t" \ |
|
||||
"rsb %[bi], %[bi], #0\n\t" \ |
|
||||
"smull %[br], %[mr], r0, %[br]\n\t" \ |
|
||||
"mov %[tt], %[tt], lsr #17\n\t" \ |
|
||||
"smlal %[br], %[mr], r1, %[bi]\n\t" \ |
|
||||
"orr %[mi], %[tt], %[mi], lsl #15\n\t" \ |
|
||||
"mov %[br], %[br], lsr #17\n\t" \ |
|
||||
"orr %[mr], %[br], %[mr], lsl #15\n\t" \ |
|
||||
: [mr]"=r"((m).r), [mi]"=r"((m).i), \ |
|
||||
[br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ |
|
||||
: [ap]"r"(&(a)), [bp]"r"(&(b)) \ |
|
||||
: "r0", "r1" \ |
|
||||
); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#undef C_MULC |
|
||||
#define C_MULC(m,a,b) \ |
|
||||
do{ \ |
|
||||
int br__; \ |
|
||||
int bi__; \ |
|
||||
int tt__; \ |
|
||||
__asm__ __volatile__( \ |
|
||||
"#C_MULC\n\t" \ |
|
||||
"ldrsh %[br], [%[bp], #0]\n\t" \ |
|
||||
"ldm %[ap], {r0,r1}\n\t" \ |
|
||||
"ldrsh %[bi], [%[bp], #2]\n\t" \ |
|
||||
"smull %[tt], %[mr], r0, %[br]\n\t" \ |
|
||||
"smlal %[tt], %[mr], r1, %[bi]\n\t" \ |
|
||||
"rsb %[bi], %[bi], #0\n\t" \ |
|
||||
"smull %[br], %[mi], r1, %[br]\n\t" \ |
|
||||
"mov %[tt], %[tt], lsr #15\n\t" \ |
|
||||
"smlal %[br], %[mi], r0, %[bi]\n\t" \ |
|
||||
"orr %[mr], %[tt], %[mr], lsl #17\n\t" \ |
|
||||
"mov %[br], %[br], lsr #15\n\t" \ |
|
||||
"orr %[mi], %[br], %[mi], lsl #17\n\t" \ |
|
||||
: [mr]"=r"((m).r), [mi]"=r"((m).i), \ |
|
||||
[br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ |
|
||||
: [ap]"r"(&(a)), [bp]"r"(&(b)) \ |
|
||||
: "r0", "r1" \ |
|
||||
); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#endif /* FIXED_POINT */ |
|
||||
|
|
||||
#endif /* KISS_FFT_ARMv4_H */ |
|
||||
@ -1,118 +0,0 @@ |
|||||
/*Copyright (c) 2013, Xiph.Org Foundation and contributors.
|
|
||||
|
|
||||
All rights reserved. |
|
||||
|
|
||||
Redistribution and use in source and binary forms, with or without |
|
||||
modification, are permitted provided that the following conditions are met: |
|
||||
|
|
||||
* Redistributions of source code must retain the above copyright notice, |
|
||||
this list of conditions and the following disclaimer. |
|
||||
* Redistributions in binary form must reproduce the above copyright notice, |
|
||||
this list of conditions and the following disclaimer in the |
|
||||
documentation and/or other materials provided with the distribution. |
|
||||
|
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
||||
POSSIBILITY OF SUCH DAMAGE.*/ |
|
||||
|
|
||||
#ifndef KISS_FFT_ARMv5E_H |
|
||||
#define KISS_FFT_ARMv5E_H |
|
||||
|
|
||||
#if !defined(KISS_FFT_GUTS_H) |
|
||||
#error "This file should only be included from _kiss_fft_guts.h" |
|
||||
#endif |
|
||||
|
|
||||
#ifdef FIXED_POINT |
|
||||
|
|
||||
#if defined(__thumb__)||defined(__thumb2__) |
|
||||
#define LDRD_CONS "Q" |
|
||||
#else |
|
||||
#define LDRD_CONS "Uq" |
|
||||
#endif |
|
||||
|
|
||||
#undef C_MUL |
|
||||
#define C_MUL(m,a,b) \ |
|
||||
do{ \ |
|
||||
int mr1__; \ |
|
||||
int mr2__; \ |
|
||||
int mi__; \ |
|
||||
long long aval__; \ |
|
||||
int bval__; \ |
|
||||
__asm__( \ |
|
||||
"#C_MUL\n\t" \ |
|
||||
"ldrd %[aval], %H[aval], %[ap]\n\t" \ |
|
||||
"ldr %[bval], %[bp]\n\t" \ |
|
||||
"smulwb %[mi], %H[aval], %[bval]\n\t" \ |
|
||||
"smulwb %[mr1], %[aval], %[bval]\n\t" \ |
|
||||
"smulwt %[mr2], %H[aval], %[bval]\n\t" \ |
|
||||
"smlawt %[mi], %[aval], %[bval], %[mi]\n\t" \ |
|
||||
: [mr1]"=r"(mr1__), [mr2]"=r"(mr2__), [mi]"=r"(mi__), \ |
|
||||
[aval]"=&r"(aval__), [bval]"=r"(bval__) \ |
|
||||
: [ap]LDRD_CONS(a), [bp]"m"(b) \ |
|
||||
); \ |
|
||||
(m).r = SHL32(SUB32(mr1__, mr2__), 1); \ |
|
||||
(m).i = SHL32(mi__, 1); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#undef C_MUL4 |
|
||||
#define C_MUL4(m,a,b) \ |
|
||||
do{ \ |
|
||||
int mr1__; \ |
|
||||
int mr2__; \ |
|
||||
int mi__; \ |
|
||||
long long aval__; \ |
|
||||
int bval__; \ |
|
||||
__asm__( \ |
|
||||
"#C_MUL4\n\t" \ |
|
||||
"ldrd %[aval], %H[aval], %[ap]\n\t" \ |
|
||||
"ldr %[bval], %[bp]\n\t" \ |
|
||||
"smulwb %[mi], %H[aval], %[bval]\n\t" \ |
|
||||
"smulwb %[mr1], %[aval], %[bval]\n\t" \ |
|
||||
"smulwt %[mr2], %H[aval], %[bval]\n\t" \ |
|
||||
"smlawt %[mi], %[aval], %[bval], %[mi]\n\t" \ |
|
||||
: [mr1]"=r"(mr1__), [mr2]"=r"(mr2__), [mi]"=r"(mi__), \ |
|
||||
[aval]"=&r"(aval__), [bval]"=r"(bval__) \ |
|
||||
: [ap]LDRD_CONS(a), [bp]"m"(b) \ |
|
||||
); \ |
|
||||
(m).r = SHR32(SUB32(mr1__, mr2__), 1); \ |
|
||||
(m).i = SHR32(mi__, 1); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#undef C_MULC |
|
||||
#define C_MULC(m,a,b) \ |
|
||||
do{ \ |
|
||||
int mr__; \ |
|
||||
int mi1__; \ |
|
||||
int mi2__; \ |
|
||||
long long aval__; \ |
|
||||
int bval__; \ |
|
||||
__asm__( \ |
|
||||
"#C_MULC\n\t" \ |
|
||||
"ldrd %[aval], %H[aval], %[ap]\n\t" \ |
|
||||
"ldr %[bval], %[bp]\n\t" \ |
|
||||
"smulwb %[mr], %[aval], %[bval]\n\t" \ |
|
||||
"smulwb %[mi1], %H[aval], %[bval]\n\t" \ |
|
||||
"smulwt %[mi2], %[aval], %[bval]\n\t" \ |
|
||||
"smlawt %[mr], %H[aval], %[bval], %[mr]\n\t" \ |
|
||||
: [mr]"=r"(mr__), [mi1]"=r"(mi1__), [mi2]"=r"(mi2__), \ |
|
||||
[aval]"=&r"(aval__), [bval]"=r"(bval__) \ |
|
||||
: [ap]LDRD_CONS(a), [bp]"m"(b) \ |
|
||||
); \ |
|
||||
(m).r = SHL32(mr__, 1); \ |
|
||||
(m).i = SHL32(SUB32(mi1__, mi2__), 1); \ |
|
||||
} \ |
|
||||
while(0) |
|
||||
|
|
||||
#endif /* FIXED_POINT */ |
|
||||
|
|
||||
#endif /* KISS_FFT_GUTS_H */ |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue