539 changed files with 687794 additions and 339 deletions
@ -0,0 +1,45 @@ |
|||
package com.yunqiinnovation.deepsound |
|||
|
|||
import android.content.BroadcastReceiver |
|||
import android.content.Context |
|||
import android.content.Intent |
|||
import android.os.Build |
|||
import com.yunqiinnovation.deepsound.core.utils.FileLogger |
|||
|
|||
/** |
|||
* 蓝牙BLE广播接收器 |
|||
* 用于接收系统通过PendingIntent触发的蓝牙扫描结果 |
|||
* 即使应用进程已被杀死,系统也能通过此接收器唤醒应用 |
|||
*/ |
|||
class BleBroadcastReceiver : BroadcastReceiver() { |
|||
|
|||
companion object { |
|||
private const val TAG = "BleBroadcastReceiver" |
|||
|
|||
// 蓝牙扫描结果Action |
|||
const val ACTION_BLE_SCAN_RESULT = "com.yunqiinnovation.deepsound.ACTION_BLE_SCAN_RESULT" |
|||
} |
|||
|
|||
override fun onReceive(context: Context, intent: Intent) { |
|||
FileLogger.init(context) |
|||
//FileLogger.d(TAG, "接收到广播: ${intent.action}") |
|||
|
|||
if (intent.action == ACTION_BLE_SCAN_RESULT) { |
|||
//FileLogger.d(TAG, "收到系统BLE扫描唤醒事件") |
|||
|
|||
// 打印日志,表明唤醒成功 |
|||
//FileLogger.d(TAG, "应用被蓝牙BLE广播唤醒成功") |
|||
|
|||
// 启动BLE唤醒服务,该服务将执行后续操作 |
|||
val serviceIntent = Intent(context, BleWakeService::class.java) |
|||
serviceIntent.action = "android.bluetooth.adapter.action.REQUEST_ENABLE" |
|||
|
|||
// 对于Android 8.0及以上版本,必须使用startForegroundService启动前台服务 |
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|||
context.startForegroundService(serviceIntent) |
|||
} else { |
|||
context.startService(serviceIntent) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,335 @@ |
|||
package com.yunqiinnovation.deepsound |
|||
|
|||
import android.app.Notification |
|||
import android.app.NotificationChannel |
|||
import android.app.NotificationManager |
|||
import android.app.PendingIntent |
|||
import android.app.Service |
|||
import android.bluetooth.BluetoothAdapter |
|||
import android.bluetooth.BluetoothManager |
|||
import android.bluetooth.le.BluetoothLeScanner |
|||
import android.bluetooth.le.ScanCallback |
|||
import android.bluetooth.le.ScanFilter |
|||
import android.bluetooth.le.ScanResult |
|||
import android.bluetooth.le.ScanSettings |
|||
import android.content.Context |
|||
import android.content.Intent |
|||
import android.os.Binder |
|||
import android.os.Build |
|||
import android.os.IBinder |
|||
import android.os.ParcelUuid |
|||
import androidx.core.app.NotificationCompat |
|||
import com.yunqiinnovation.deepsound.core.utils.FileLogger |
|||
import java.util.UUID |
|||
|
|||
/** |
|||
* BLE唤醒服务,用于接收蓝牙BLE广播唤醒应用 |
|||
* 可以作为前台服务运行,以提高可靠性 |
|||
*/ |
|||
class BleWakeService : Service() { |
|||
|
|||
companion object { |
|||
private const val TAG = "BleWakeService" |
|||
private const val NOTIFICATION_ID = 1002 |
|||
private const val CHANNEL_ID = "ble_wake_channel" |
|||
|
|||
// 定义唤醒用的特定UUID,应与耳机约定一致 |
|||
// 此UUID仅作示例,实际使用时应根据耳机厂商提供的UUID进行修改 |
|||
val OLA_FRIEND_UUID = UUID.fromString("0000fd7b-0000-1000-8000-00805f9b34fb") |
|||
|
|||
// 服务启动标志 |
|||
private var isRunning = false |
|||
|
|||
// 获取服务运行状态 |
|||
fun isServiceRunning(): Boolean = isRunning |
|||
} |
|||
|
|||
private val binder = LocalBinder() |
|||
private var bluetoothAdapter: BluetoothAdapter? = null |
|||
private var bluetoothLeScanner: BluetoothLeScanner? = null |
|||
private var isScanning = false |
|||
|
|||
/** |
|||
* 本地绑定器 |
|||
*/ |
|||
inner class LocalBinder : Binder() { |
|||
fun getService(): BleWakeService = this@BleWakeService |
|||
} |
|||
|
|||
override fun onCreate() { |
|||
super.onCreate() |
|||
FileLogger.d(TAG, "BLE唤醒服务正在创建") |
|||
|
|||
// 初始化蓝牙 |
|||
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager |
|||
bluetoothAdapter = bluetoothManager.adapter |
|||
bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner |
|||
|
|||
// 标记服务状态 |
|||
isRunning = true |
|||
|
|||
// 启动前台服务 |
|||
startForeground(NOTIFICATION_ID, createNotification()) |
|||
|
|||
// 开始扫描 |
|||
// startScanning() |
|||
|
|||
FileLogger.d(TAG, "BLE唤醒服务已启动") |
|||
} |
|||
|
|||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { |
|||
// FileLogger.d(TAG, "收到onStartCommand: intent=$intent") |
|||
|
|||
// 如果是PendingIntent触发(BLE广播唤醒),打印日志 |
|||
if (intent?.action == "android.bluetooth.adapter.action.REQUEST_ENABLE") { |
|||
//FileLogger.d(TAG, "收到BLE广播唤醒事件,应用被唤醒") |
|||
|
|||
// 在这里可以执行唤醒后的动作,如启动语音交互服务 |
|||
val serviceIntent = Intent(this, VoiceInteractionService::class.java) |
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|||
startForegroundService(serviceIntent) |
|||
} else { |
|||
startService(serviceIntent) |
|||
} |
|||
} |
|||
|
|||
return START_STICKY |
|||
} |
|||
|
|||
override fun onBind(intent: Intent?): IBinder { |
|||
return binder |
|||
} |
|||
|
|||
override fun onDestroy() { |
|||
super.onDestroy() |
|||
FileLogger.d(TAG, "BLE唤醒服务即将销毁") |
|||
|
|||
// 停止扫描 |
|||
stopScanning() |
|||
|
|||
// 标记服务状态 |
|||
isRunning = false |
|||
} |
|||
|
|||
/** |
|||
* 开始扫描BLE广播 |
|||
*/ |
|||
fun startScanning() { |
|||
FileLogger.d(TAG, "开始扫描BLE广播") |
|||
|
|||
if (bluetoothLeScanner == null) { |
|||
FileLogger.e(TAG, "蓝牙扫描器不可用") |
|||
return |
|||
} |
|||
|
|||
if (isScanning) { |
|||
FileLogger.d(TAG, "已在扫描中,忽略") |
|||
return |
|||
} |
|||
|
|||
// 构建扫描过滤器,匹配特定的UUID |
|||
val scanFilter = ScanFilter.Builder() |
|||
.setServiceUuid(ParcelUuid(OLA_FRIEND_UUID)) |
|||
.build() |
|||
|
|||
// 设置扫描参数 |
|||
val scanSettings = ScanSettings.Builder() |
|||
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // 低功耗模式 |
|||
.build() |
|||
|
|||
try { |
|||
FileLogger.d(TAG, "开始BLE扫描, 扫描过滤器: $OLA_FRIEND_UUID") |
|||
// 开始扫描 |
|||
bluetoothLeScanner?.startScan(listOf(scanFilter), scanSettings, scanCallback) |
|||
isScanning = true |
|||
FileLogger.d(TAG, "BLE扫描已启动") |
|||
} catch (e: Exception) { |
|||
FileLogger.e(TAG, "开始BLE扫描失败: ${e.message}") |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 停止扫描BLE广播 |
|||
*/ |
|||
fun stopScanning() { |
|||
FileLogger.d(TAG, "停止扫描BLE广播") |
|||
|
|||
if (bluetoothLeScanner == null || !isScanning) { |
|||
FileLogger.d(TAG, "没有正在进行的扫描") |
|||
return |
|||
} |
|||
|
|||
try { |
|||
// 停止扫描 |
|||
bluetoothLeScanner?.stopScan(scanCallback) |
|||
isScanning = false |
|||
FileLogger.d(TAG, "BLE扫描已停止") |
|||
} catch (e: Exception) { |
|||
FileLogger.e(TAG, "停止BLE扫描失败: ${e.message}") |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 扫描回调 |
|||
*/ |
|||
private val scanCallback = object : ScanCallback() { |
|||
override fun onScanResult(callbackType: Int, result: ScanResult) { |
|||
FileLogger.d(TAG, "收到扫描结果: ${result.device.address}") |
|||
|
|||
// 检查是否是目标设备和唤醒广播 |
|||
val serviceUuids = result.scanRecord?.serviceUuids |
|||
if (serviceUuids != null && serviceUuids.any { it.uuid == OLA_FRIEND_UUID }) { |
|||
FileLogger.d(TAG, "检测到Ola Friend耳机唤醒广播") |
|||
|
|||
|
|||
|
|||
// 从广播包中获取额外数据 |
|||
val manufacturerData = result.scanRecord?.getManufacturerSpecificData(0xFFFF) |
|||
if (manufacturerData != null && manufacturerData.size > 1) { |
|||
// 假设第一个字节为命令类型,如0x01表示语音唤醒 |
|||
val commandType = manufacturerData[0].toInt() and 0xFF |
|||
FileLogger.d(TAG, "收到命令类型: $commandType") |
|||
|
|||
if (commandType == 0x01) { // 语音唤醒命令 |
|||
FileLogger.d(TAG, "耳机语音唤醒命令,启动语音交互") |
|||
|
|||
// 启动语音交互服务 |
|||
val serviceIntent = Intent(applicationContext, VoiceInteractionService::class.java) |
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|||
startForegroundService(serviceIntent) |
|||
} else { |
|||
startService(serviceIntent) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
override fun onScanFailed(errorCode: Int) { |
|||
FileLogger.e(TAG, "BLE扫描失败,错误代码: $errorCode") |
|||
isScanning = false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建通知 |
|||
*/ |
|||
private fun createNotification(): Notification { |
|||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
|||
|
|||
// Android 8.0+ 需要创建通知渠道 |
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|||
val channel = NotificationChannel( |
|||
CHANNEL_ID, |
|||
"蓝牙唤醒服务", |
|||
NotificationManager.IMPORTANCE_LOW |
|||
) |
|||
manager.createNotificationChannel(channel) |
|||
} |
|||
|
|||
// 点击通知打开MainActivity |
|||
val intent = Intent(this, MainActivity::class.java).apply { |
|||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP |
|||
} |
|||
val pendingIntent = PendingIntent.getActivity( |
|||
this, |
|||
0, |
|||
intent, |
|||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT |
|||
) |
|||
|
|||
return NotificationCompat.Builder(this, CHANNEL_ID) |
|||
.setContentTitle("蓝牙唤醒服务") |
|||
.setContentText("正在监听耳机唤醒信号") |
|||
.setSmallIcon(R.mipmap.ic_launcher) |
|||
.setContentIntent(pendingIntent) |
|||
.build() |
|||
} |
|||
|
|||
/** |
|||
* 为后台持久扫描注册PendingIntent |
|||
* 这种方式即使应用被销毁后仍然可被唤醒 |
|||
*/ |
|||
fun registerBackgroundScan() { |
|||
FileLogger.d(TAG, "注册后台持久扫描") |
|||
|
|||
if (bluetoothLeScanner == null) { |
|||
FileLogger.e(TAG, "蓝牙扫描器不可用") |
|||
return |
|||
} |
|||
|
|||
// 构建扫描过滤器,匹配特定的UUID |
|||
val scanFilter = ScanFilter.Builder() |
|||
// .setServiceUuid(ParcelUuid(OLA_FRIEND_UUID)) |
|||
.build() |
|||
|
|||
// 设置扫描参数 |
|||
val scanSettings = ScanSettings.Builder() |
|||
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // 低功耗模式 |
|||
.build() |
|||
|
|||
// 创建PendingIntent |
|||
val intent = Intent(this, BleBroadcastReceiver::class.java) |
|||
intent.action = BleBroadcastReceiver.ACTION_BLE_SCAN_RESULT |
|||
|
|||
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|||
PendingIntent.getBroadcast( |
|||
this, 0, intent, |
|||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|||
) |
|||
} else { |
|||
PendingIntent.getBroadcast( |
|||
this, 0, intent, |
|||
PendingIntent.FLAG_UPDATE_CURRENT |
|||
) |
|||
} |
|||
|
|||
try { |
|||
// 启动带PendingIntent的扫描 |
|||
bluetoothLeScanner?.startScan( |
|||
listOf(scanFilter), |
|||
scanSettings, |
|||
pendingIntent |
|||
) |
|||
FileLogger.d(TAG, "后台持久扫描已注册") |
|||
} catch (e: Exception) { |
|||
FileLogger.e(TAG, "注册后台持久扫描失败: ${e.message}") |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 注销后台持久扫描 |
|||
*/ |
|||
fun unregisterBackgroundScan() { |
|||
FileLogger.d(TAG, "注销后台持久扫描") |
|||
|
|||
if (bluetoothLeScanner == null) { |
|||
FileLogger.e(TAG, "蓝牙扫描器不可用") |
|||
return |
|||
} |
|||
|
|||
// 创建与注册时相同的PendingIntent |
|||
val intent = Intent(this, BleBroadcastReceiver::class.java) |
|||
intent.action = BleBroadcastReceiver.ACTION_BLE_SCAN_RESULT |
|||
|
|||
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|||
PendingIntent.getBroadcast( |
|||
this, 0, intent, |
|||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|||
) |
|||
} else { |
|||
PendingIntent.getBroadcast( |
|||
this, 0, intent, |
|||
PendingIntent.FLAG_UPDATE_CURRENT |
|||
) |
|||
} |
|||
|
|||
try { |
|||
// 停止带PendingIntent的扫描 |
|||
bluetoothLeScanner?.stopScan(pendingIntent) |
|||
FileLogger.d(TAG, "后台持久扫描已注销") |
|||
} catch (e: Exception) { |
|||
FileLogger.e(TAG, "注销后台持久扫描失败: ${e.message}") |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/opus_test_controller.dart'; |
|||
|
|||
class OpusTestBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.lazyPut<OpusTestController>(() => OpusTestController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,404 @@ |
|||
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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,445 @@ |
|||
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, |
|||
), |
|||
], |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,126 @@ |
|||
# 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平台 |
|||
@ -0,0 +1,62 @@ |
|||
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 = 33 |
|||
|
|||
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") |
|||
} |
|||
|
|||
@ -0,0 +1 @@ |
|||
rootProject.name = "jl_opus" |
|||
@ -0,0 +1,9 @@ |
|||
<?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> |
|||
@ -0,0 +1,377 @@ |
|||
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 Callback1 { |
|||
|
|||
fun onAudioDataReceived1(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: Callback1 |
|||
|
|||
) { |
|||
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.onAudioDataReceived1(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.
@ -0,0 +1,221 @@ |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
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 |
|||
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.
@ -0,0 +1,2 @@ |
|||
#Tue Apr 15 09:36:50 IST 2025 |
|||
gradle.version=8.10 |
|||
@ -0,0 +1 @@ |
|||
C7E6339E39539FBB82728828DAB8AC59D6F180C3E383F85B403CD9B85CA1DF6A |
|||
@ -0,0 +1,92 @@ |
|||
# 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) 目录中的示例代码,了解如何使用此插件生成、编码和解码音频数据。 |
|||
@ -0,0 +1,21 @@ |
|||
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) |
|||
@ -0,0 +1,67 @@ |
|||
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 = 31 |
|||
|
|||
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") |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
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) |
|||
@ -0,0 +1,744 @@ |
|||
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() |
|||
@ -0,0 +1,44 @@ |
|||
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/ |
|||
@ -0,0 +1,368 @@ |
|||
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. |
|||
@ -0,0 +1,489 @@ |
|||
# 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
@ -0,0 +1,169 @@ |
|||
#################### 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 |
|||
@ -0,0 +1,167 @@ |
|||
#################### 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 |
|||
@ -0,0 +1,189 @@ |
|||
== 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
@ -0,0 +1,182 @@ |
|||
/*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 */ |
|||
@ -0,0 +1,291 @@ |
|||
/* 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 */ |
|||
@ -0,0 +1,353 @@ |
|||
#!/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"); |
|||
} |
|||
@ -0,0 +1,171 @@ |
|||
/* 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 */ |
|||
@ -0,0 +1,291 @@ |
|||
/* 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 |
|||
@ -0,0 +1,90 @@ |
|||
/* 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 |
|||
@ -0,0 +1,37 @@ |
|||
/* 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 |
|||
@ -0,0 +1,173 @@ |
|||
/* 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; |
|||
} |
|||
@ -0,0 +1,258 @@ |
|||
/* 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; |
|||
} |
|||
@ -0,0 +1,272 @@ |
|||
/* 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 |
|||
@ -0,0 +1,555 @@ |
|||
.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 |
|||
@ -0,0 +1,551 @@ |
|||
; 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 |
|||
@ -0,0 +1,71 @@ |
|||
/* 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 |
|||
@ -0,0 +1,35 @@ |
|||
/* 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 |
|||
@ -0,0 +1,80 @@ |
|||
/* 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 |
|||
@ -0,0 +1,151 @@ |
|||
/* 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 |
|||
@ -0,0 +1,121 @@ |
|||
/*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 */ |
|||
@ -0,0 +1,118 @@ |
|||
/*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 */ |
|||
@ -0,0 +1,59 @@ |
|||
/* Copyright (c) 2015 Xiph.Org Foundation
|
|||
Written by Viswanath Puttagunta */ |
|||
/**
|
|||
@file arm_mdct.h |
|||
@brief ARM Neon Intrinsic 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. |
|||
*/ |
|||
|
|||
#if !defined(MDCT_ARM_H) |
|||
#define MDCT_ARM_H |
|||
|
|||
#include "mdct.h" |
|||
|
|||
#if defined(HAVE_ARM_NE10) |
|||
/** Compute a forward MDCT and scale by 4/N, trashes the input array */ |
|||
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); |
|||
|
|||
void clt_mdct_backward_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); |
|||
|
|||
#if !defined(OPUS_HAVE_RTCD) |
|||
#define OVERRIDE_OPUS_MDCT (1) |
|||
#define clt_mdct_forward(_l, _in, _out, _window, _int, _shift, _stride, _arch) \ |
|||
clt_mdct_forward_neon(_l, _in, _out, _window, _int, _shift, _stride, _arch) |
|||
#define clt_mdct_backward(_l, _in, _out, _window, _int, _shift, _stride, _arch) \ |
|||
clt_mdct_backward_neon(_l, _in, _out, _window, _int, _shift, _stride, _arch) |
|||
#endif /* OPUS_HAVE_RTCD */ |
|||
#endif /* HAVE_ARM_NE10 */ |
|||
|
|||
#endif |
|||
@ -0,0 +1,160 @@ |
|||
/* 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(PITCH_ARM_H) |
|||
# define PITCH_ARM_H |
|||
|
|||
# include "armcpu.h" |
|||
|
|||
# if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|||
opus_val32 celt_inner_prod_neon(const opus_val16 *x, const opus_val16 *y, int N); |
|||
void dual_inner_prod_neon(const opus_val16 *x, const opus_val16 *y01, |
|||
const opus_val16 *y02, int N, opus_val32 *xy1, opus_val32 *xy2); |
|||
|
|||
# if !defined(OPUS_HAVE_RTCD) && defined(OPUS_ARM_PRESUME_NEON) |
|||
# define OVERRIDE_CELT_INNER_PROD (1) |
|||
# define OVERRIDE_DUAL_INNER_PROD (1) |
|||
# define celt_inner_prod(x, y, N, arch) ((void)(arch), PRESUME_NEON(celt_inner_prod)(x, y, N)) |
|||
# define dual_inner_prod(x, y01, y02, N, xy1, xy2, arch) ((void)(arch), PRESUME_NEON(dual_inner_prod)(x, y01, y02, N, xy1, xy2)) |
|||
# endif |
|||
# endif |
|||
|
|||
# if !defined(OVERRIDE_CELT_INNER_PROD) |
|||
# if defined(OPUS_HAVE_RTCD) && (defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR)) |
|||
extern opus_val32 (*const CELT_INNER_PROD_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *x, const opus_val16 *y, int N); |
|||
# define OVERRIDE_CELT_INNER_PROD (1) |
|||
# define celt_inner_prod(x, y, N, arch) ((*CELT_INNER_PROD_IMPL[(arch)&OPUS_ARCHMASK])(x, y, N)) |
|||
# elif defined(OPUS_ARM_PRESUME_NEON_INTR) |
|||
# define OVERRIDE_CELT_INNER_PROD (1) |
|||
# define celt_inner_prod(x, y, N, arch) ((void)(arch), celt_inner_prod_neon(x, y, N)) |
|||
# endif |
|||
# endif |
|||
|
|||
# if !defined(OVERRIDE_DUAL_INNER_PROD) |
|||
# if defined(OPUS_HAVE_RTCD) && (defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR)) |
|||
extern 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); |
|||
# define OVERRIDE_DUAL_INNER_PROD (1) |
|||
# define dual_inner_prod(x, y01, y02, N, xy1, xy2, arch) ((*DUAL_INNER_PROD_IMPL[(arch)&OPUS_ARCHMASK])(x, y01, y02, N, xy1, xy2)) |
|||
# elif defined(OPUS_ARM_PRESUME_NEON_INTR) |
|||
# define OVERRIDE_DUAL_INNER_PROD (1) |
|||
# define dual_inner_prod(x, y01, y02, N, xy1, xy2, arch) ((void)(arch), dual_inner_prod_neon(x, y01, y02, N, xy1, xy2)) |
|||
# endif |
|||
# endif |
|||
|
|||
# if defined(FIXED_POINT) |
|||
|
|||
# if defined(OPUS_ARM_MAY_HAVE_NEON) |
|||
opus_val32 celt_pitch_xcorr_neon(const opus_val16 *_x, const opus_val16 *_y, |
|||
opus_val32 *xcorr, int len, int max_pitch, int arch); |
|||
# endif |
|||
|
|||
# if defined(OPUS_ARM_MAY_HAVE_MEDIA) |
|||
# define celt_pitch_xcorr_media MAY_HAVE_EDSP(celt_pitch_xcorr) |
|||
# endif |
|||
|
|||
# if defined(OPUS_ARM_MAY_HAVE_EDSP) |
|||
opus_val32 celt_pitch_xcorr_edsp(const opus_val16 *_x, const opus_val16 *_y, |
|||
opus_val32 *xcorr, int len, int max_pitch, int arch); |
|||
# endif |
|||
|
|||
# if defined(OPUS_HAVE_RTCD) && \ |
|||
((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))) |
|||
extern opus_val32 |
|||
(*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, |
|||
const opus_val16 *, opus_val32 *, int, int, int); |
|||
# define OVERRIDE_PITCH_XCORR (1) |
|||
# define celt_pitch_xcorr(_x, _y, xcorr, len, max_pitch, arch) \ |
|||
((*CELT_PITCH_XCORR_IMPL[(arch)&OPUS_ARCHMASK])(_x, _y, \ |
|||
xcorr, len, max_pitch, arch)) |
|||
|
|||
# elif defined(OPUS_ARM_PRESUME_EDSP) || \ |
|||
defined(OPUS_ARM_PRESUME_MEDIA) || \ |
|||
defined(OPUS_ARM_PRESUME_NEON) |
|||
# define OVERRIDE_PITCH_XCORR (1) |
|||
# define celt_pitch_xcorr (PRESUME_NEON(celt_pitch_xcorr)) |
|||
|
|||
# endif |
|||
|
|||
# if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|||
void xcorr_kernel_neon_fixed( |
|||
const opus_val16 *x, |
|||
const opus_val16 *y, |
|||
opus_val32 sum[4], |
|||
int len); |
|||
# endif |
|||
|
|||
# if defined(OPUS_HAVE_RTCD) && \ |
|||
(defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR)) |
|||
|
|||
extern void (*const XCORR_KERNEL_IMPL[OPUS_ARCHMASK + 1])( |
|||
const opus_val16 *x, |
|||
const opus_val16 *y, |
|||
opus_val32 sum[4], |
|||
int len); |
|||
|
|||
# define OVERRIDE_XCORR_KERNEL (1) |
|||
# define xcorr_kernel(x, y, sum, len, arch) \ |
|||
((*XCORR_KERNEL_IMPL[(arch) & OPUS_ARCHMASK])(x, y, sum, len)) |
|||
|
|||
# elif defined(OPUS_ARM_PRESUME_NEON_INTR) |
|||
# define OVERRIDE_XCORR_KERNEL (1) |
|||
# define xcorr_kernel(x, y, sum, len, arch) \ |
|||
((void)arch, xcorr_kernel_neon_fixed(x, y, sum, len)) |
|||
|
|||
# endif |
|||
|
|||
#else /* Start !FIXED_POINT */ |
|||
/* Float case */ |
|||
#if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) |
|||
void celt_pitch_xcorr_float_neon(const opus_val16 *_x, const opus_val16 *_y, |
|||
opus_val32 *xcorr, int len, int max_pitch, int arch); |
|||
#endif |
|||
|
|||
# if defined(OPUS_HAVE_RTCD) && \ |
|||
(defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR)) |
|||
extern void |
|||
(*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, |
|||
const opus_val16 *, opus_val32 *, int, int, int); |
|||
|
|||
# define OVERRIDE_PITCH_XCORR (1) |
|||
# define celt_pitch_xcorr(_x, _y, xcorr, len, max_pitch, arch) \ |
|||
((*CELT_PITCH_XCORR_IMPL[(arch)&OPUS_ARCHMASK])(_x, _y, \ |
|||
xcorr, len, max_pitch, arch)) |
|||
|
|||
# elif defined(OPUS_ARM_PRESUME_NEON_INTR) |
|||
|
|||
# define OVERRIDE_PITCH_XCORR (1) |
|||
# define celt_pitch_xcorr celt_pitch_xcorr_float_neon |
|||
|
|||
# endif |
|||
|
|||
#endif /* end !FIXED_POINT */ |
|||
|
|||
#endif |
|||
@ -0,0 +1,288 @@ |
|||
/***********************************************************************
|
|||
Copyright (c) 2017 Google Inc. |
|||
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. |
|||
***********************************************************************/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#include <arm_neon.h> |
|||
#include "pitch.h" |
|||
|
|||
#ifdef FIXED_POINT |
|||
|
|||
opus_val32 celt_inner_prod_neon(const opus_val16 *x, const opus_val16 *y, int N) |
|||
{ |
|||
int i; |
|||
opus_val32 xy; |
|||
int16x8_t x_s16x8, y_s16x8; |
|||
int32x4_t xy_s32x4 = vdupq_n_s32(0); |
|||
int64x2_t xy_s64x2; |
|||
int64x1_t xy_s64x1; |
|||
|
|||
for (i = 0; i < N - 7; i += 8) { |
|||
x_s16x8 = vld1q_s16(&x[i]); |
|||
y_s16x8 = vld1q_s16(&y[i]); |
|||
xy_s32x4 = vmlal_s16(xy_s32x4, vget_low_s16 (x_s16x8), vget_low_s16 (y_s16x8)); |
|||
xy_s32x4 = vmlal_s16(xy_s32x4, vget_high_s16(x_s16x8), vget_high_s16(y_s16x8)); |
|||
} |
|||
|
|||
if (N - i >= 4) { |
|||
const int16x4_t x_s16x4 = vld1_s16(&x[i]); |
|||
const int16x4_t y_s16x4 = vld1_s16(&y[i]); |
|||
xy_s32x4 = vmlal_s16(xy_s32x4, x_s16x4, y_s16x4); |
|||
i += 4; |
|||
} |
|||
|
|||
xy_s64x2 = vpaddlq_s32(xy_s32x4); |
|||
xy_s64x1 = vadd_s64(vget_low_s64(xy_s64x2), vget_high_s64(xy_s64x2)); |
|||
xy = vget_lane_s32(vreinterpret_s32_s64(xy_s64x1), 0); |
|||
|
|||
for (; i < N; i++) { |
|||
xy = MAC16_16(xy, x[i], y[i]); |
|||
} |
|||
|
|||
#ifdef OPUS_CHECK_ASM |
|||
celt_assert(celt_inner_prod_c(x, y, N) == xy); |
|||
#endif |
|||
|
|||
return xy; |
|||
} |
|||
|
|||
void dual_inner_prod_neon(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, |
|||
int N, opus_val32 *xy1, opus_val32 *xy2) |
|||
{ |
|||
int i; |
|||
opus_val32 xy01, xy02; |
|||
int16x8_t x_s16x8, y01_s16x8, y02_s16x8; |
|||
int32x4_t xy01_s32x4 = vdupq_n_s32(0); |
|||
int32x4_t xy02_s32x4 = vdupq_n_s32(0); |
|||
int64x2_t xy01_s64x2, xy02_s64x2; |
|||
int64x1_t xy01_s64x1, xy02_s64x1; |
|||
|
|||
for (i = 0; i < N - 7; i += 8) { |
|||
x_s16x8 = vld1q_s16(&x[i]); |
|||
y01_s16x8 = vld1q_s16(&y01[i]); |
|||
y02_s16x8 = vld1q_s16(&y02[i]); |
|||
xy01_s32x4 = vmlal_s16(xy01_s32x4, vget_low_s16 (x_s16x8), vget_low_s16 (y01_s16x8)); |
|||
xy02_s32x4 = vmlal_s16(xy02_s32x4, vget_low_s16 (x_s16x8), vget_low_s16 (y02_s16x8)); |
|||
xy01_s32x4 = vmlal_s16(xy01_s32x4, vget_high_s16(x_s16x8), vget_high_s16(y01_s16x8)); |
|||
xy02_s32x4 = vmlal_s16(xy02_s32x4, vget_high_s16(x_s16x8), vget_high_s16(y02_s16x8)); |
|||
} |
|||
|
|||
if (N - i >= 4) { |
|||
const int16x4_t x_s16x4 = vld1_s16(&x[i]); |
|||
const int16x4_t y01_s16x4 = vld1_s16(&y01[i]); |
|||
const int16x4_t y02_s16x4 = vld1_s16(&y02[i]); |
|||
xy01_s32x4 = vmlal_s16(xy01_s32x4, x_s16x4, y01_s16x4); |
|||
xy02_s32x4 = vmlal_s16(xy02_s32x4, x_s16x4, y02_s16x4); |
|||
i += 4; |
|||
} |
|||
|
|||
xy01_s64x2 = vpaddlq_s32(xy01_s32x4); |
|||
xy02_s64x2 = vpaddlq_s32(xy02_s32x4); |
|||
xy01_s64x1 = vadd_s64(vget_low_s64(xy01_s64x2), vget_high_s64(xy01_s64x2)); |
|||
xy02_s64x1 = vadd_s64(vget_low_s64(xy02_s64x2), vget_high_s64(xy02_s64x2)); |
|||
xy01 = vget_lane_s32(vreinterpret_s32_s64(xy01_s64x1), 0); |
|||
xy02 = vget_lane_s32(vreinterpret_s32_s64(xy02_s64x1), 0); |
|||
|
|||
for (; i < N; i++) { |
|||
xy01 = MAC16_16(xy01, x[i], y01[i]); |
|||
xy02 = MAC16_16(xy02, x[i], y02[i]); |
|||
} |
|||
*xy1 = xy01; |
|||
*xy2 = xy02; |
|||
|
|||
#ifdef OPUS_CHECK_ASM |
|||
{ |
|||
opus_val32 xy1_c, xy2_c; |
|||
dual_inner_prod_c(x, y01, y02, N, &xy1_c, &xy2_c); |
|||
celt_assert(xy1_c == *xy1); |
|||
celt_assert(xy2_c == *xy2); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
#else /* !FIXED_POINT */ |
|||
|
|||
/* ========================================================================== */ |
|||
|
|||
#ifdef __ARM_FEATURE_FMA |
|||
/* If we can, force the compiler to use an FMA instruction rather than break
|
|||
vmlaq_f32() into fmul/fadd. */ |
|||
#define vmlaq_f32(a,b,c) vfmaq_f32(a,b,c) |
|||
#endif |
|||
|
|||
|
|||
#ifdef OPUS_CHECK_ASM |
|||
|
|||
/* This part of code simulates floating-point NEON operations. */ |
|||
|
|||
/* celt_inner_prod_neon_float_c_simulation() simulates the floating-point */ |
|||
/* operations of celt_inner_prod_neon(), and both functions should have bit */ |
|||
/* exact output. */ |
|||
static opus_val32 celt_inner_prod_neon_float_c_simulation(const opus_val16 *x, const opus_val16 *y, float *err, int N) |
|||
{ |
|||
int i; |
|||
*err = 0; |
|||
opus_val32 xy, xy0 = 0, xy1 = 0, xy2 = 0, xy3 = 0; |
|||
for (i = 0; i < N - 3; i += 4) { |
|||
xy0 = MAC16_16(xy0, x[i + 0], y[i + 0]); |
|||
xy1 = MAC16_16(xy1, x[i + 1], y[i + 1]); |
|||
xy2 = MAC16_16(xy2, x[i + 2], y[i + 2]); |
|||
xy3 = MAC16_16(xy3, x[i + 3], y[i + 3]); |
|||
*err += ABS32(xy0)+ABS32(xy1)+ABS32(xy2)+ABS32(xy3); |
|||
} |
|||
xy0 += xy2; |
|||
xy1 += xy3; |
|||
xy = xy0 + xy1; |
|||
*err += ABS32(xy1)+ABS32(xy0)+ABS32(xy); |
|||
for (; i < N; i++) { |
|||
xy = MAC16_16(xy, x[i], y[i]); |
|||
*err += ABS32(xy); |
|||
} |
|||
*err = *err*2e-7 + N*1e-37; |
|||
return xy; |
|||
} |
|||
|
|||
/* dual_inner_prod_neon_float_c_simulation() simulates the floating-point */ |
|||
/* operations of dual_inner_prod_neon(), and both functions should have bit */ |
|||
/* exact output. */ |
|||
static void dual_inner_prod_neon_float_c_simulation(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, |
|||
int N, opus_val32 *xy1, opus_val32 *xy2, float *err) |
|||
{ |
|||
*xy1 = celt_inner_prod_neon_float_c_simulation(x, y01, &err[0], N); |
|||
*xy2 = celt_inner_prod_neon_float_c_simulation(x, y02, &err[1], N); |
|||
} |
|||
|
|||
#endif /* OPUS_CHECK_ASM */ |
|||
|
|||
/* ========================================================================== */ |
|||
|
|||
opus_val32 celt_inner_prod_neon(const opus_val16 *x, const opus_val16 *y, int N) |
|||
{ |
|||
int i; |
|||
opus_val32 xy; |
|||
float32x4_t xy_f32x4 = vdupq_n_f32(0); |
|||
float32x2_t xy_f32x2; |
|||
|
|||
for (i = 0; i < N - 7; i += 8) { |
|||
float32x4_t x_f32x4, y_f32x4; |
|||
x_f32x4 = vld1q_f32(&x[i]); |
|||
y_f32x4 = vld1q_f32(&y[i]); |
|||
xy_f32x4 = vmlaq_f32(xy_f32x4, x_f32x4, y_f32x4); |
|||
x_f32x4 = vld1q_f32(&x[i + 4]); |
|||
y_f32x4 = vld1q_f32(&y[i + 4]); |
|||
xy_f32x4 = vmlaq_f32(xy_f32x4, x_f32x4, y_f32x4); |
|||
} |
|||
|
|||
if (N - i >= 4) { |
|||
const float32x4_t x_f32x4 = vld1q_f32(&x[i]); |
|||
const float32x4_t y_f32x4 = vld1q_f32(&y[i]); |
|||
xy_f32x4 = vmlaq_f32(xy_f32x4, x_f32x4, y_f32x4); |
|||
i += 4; |
|||
} |
|||
|
|||
xy_f32x2 = vadd_f32(vget_low_f32(xy_f32x4), vget_high_f32(xy_f32x4)); |
|||
xy_f32x2 = vpadd_f32(xy_f32x2, xy_f32x2); |
|||
xy = vget_lane_f32(xy_f32x2, 0); |
|||
|
|||
for (; i < N; i++) { |
|||
xy = MAC16_16(xy, x[i], y[i]); |
|||
} |
|||
|
|||
#ifdef OPUS_CHECK_ASM |
|||
{ |
|||
float err, res; |
|||
res = celt_inner_prod_neon_float_c_simulation(x, y, &err, N); |
|||
/*if (ABS32(res - xy) > err) fprintf(stderr, "%g %g %g\n", res, xy, err);*/ |
|||
celt_assert(ABS32(res - xy) <= err); |
|||
} |
|||
#endif |
|||
|
|||
return xy; |
|||
} |
|||
|
|||
void dual_inner_prod_neon(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, |
|||
int N, opus_val32 *xy1, opus_val32 *xy2) |
|||
{ |
|||
int i; |
|||
opus_val32 xy01, xy02; |
|||
float32x4_t xy01_f32x4 = vdupq_n_f32(0); |
|||
float32x4_t xy02_f32x4 = vdupq_n_f32(0); |
|||
float32x2_t xy01_f32x2, xy02_f32x2; |
|||
|
|||
for (i = 0; i < N - 7; i += 8) { |
|||
float32x4_t x_f32x4, y01_f32x4, y02_f32x4; |
|||
x_f32x4 = vld1q_f32(&x[i]); |
|||
y01_f32x4 = vld1q_f32(&y01[i]); |
|||
y02_f32x4 = vld1q_f32(&y02[i]); |
|||
xy01_f32x4 = vmlaq_f32(xy01_f32x4, x_f32x4, y01_f32x4); |
|||
xy02_f32x4 = vmlaq_f32(xy02_f32x4, x_f32x4, y02_f32x4); |
|||
x_f32x4 = vld1q_f32(&x[i + 4]); |
|||
y01_f32x4 = vld1q_f32(&y01[i + 4]); |
|||
y02_f32x4 = vld1q_f32(&y02[i + 4]); |
|||
xy01_f32x4 = vmlaq_f32(xy01_f32x4, x_f32x4, y01_f32x4); |
|||
xy02_f32x4 = vmlaq_f32(xy02_f32x4, x_f32x4, y02_f32x4); |
|||
} |
|||
|
|||
if (N - i >= 4) { |
|||
const float32x4_t x_f32x4 = vld1q_f32(&x[i]); |
|||
const float32x4_t y01_f32x4 = vld1q_f32(&y01[i]); |
|||
const float32x4_t y02_f32x4 = vld1q_f32(&y02[i]); |
|||
xy01_f32x4 = vmlaq_f32(xy01_f32x4, x_f32x4, y01_f32x4); |
|||
xy02_f32x4 = vmlaq_f32(xy02_f32x4, x_f32x4, y02_f32x4); |
|||
i += 4; |
|||
} |
|||
|
|||
xy01_f32x2 = vadd_f32(vget_low_f32(xy01_f32x4), vget_high_f32(xy01_f32x4)); |
|||
xy02_f32x2 = vadd_f32(vget_low_f32(xy02_f32x4), vget_high_f32(xy02_f32x4)); |
|||
xy01_f32x2 = vpadd_f32(xy01_f32x2, xy01_f32x2); |
|||
xy02_f32x2 = vpadd_f32(xy02_f32x2, xy02_f32x2); |
|||
xy01 = vget_lane_f32(xy01_f32x2, 0); |
|||
xy02 = vget_lane_f32(xy02_f32x2, 0); |
|||
|
|||
for (; i < N; i++) { |
|||
xy01 = MAC16_16(xy01, x[i], y01[i]); |
|||
xy02 = MAC16_16(xy02, x[i], y02[i]); |
|||
} |
|||
*xy1 = xy01; |
|||
*xy2 = xy02; |
|||
|
|||
#ifdef OPUS_CHECK_ASM |
|||
{ |
|||
opus_val32 xy1_c, xy2_c; |
|||
float err[2]; |
|||
dual_inner_prod_neon_float_c_simulation(x, y01, y02, N, &xy1_c, &xy2_c, err); |
|||
/*if (ABS32(xy1_c - *xy1) > err[0]) fprintf(stderr, "dual1 fail: %g %g %g\n", xy1_c, *xy1, err[0]);
|
|||
if (ABS32(xy2_c - *xy2) > err[1]) fprintf(stderr, "dual2 fail: %g %g %g\n", xy2_c, *xy2, err[1]);*/ |
|||
celt_assert(ABS32(xy1_c - *xy1) <= err[0]); |
|||
celt_assert(ABS32(xy2_c - *xy2) <= err[1]); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
#endif /* FIXED_POINT */ |
|||
File diff suppressed because it is too large
@ -0,0 +1,123 @@ |
|||
/* Copyright (c) 2007-2008 CSIRO
|
|||
Copyright (c) 2007-2009 Xiph.Org Foundation |
|||
Copyright (c) 2008-2009 Gregory Maxwell |
|||
Written by Jean-Marc Valin and Gregory Maxwell */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef BANDS_H |
|||
#define BANDS_H |
|||
|
|||
#include "arch.h" |
|||
#include "modes.h" |
|||
#include "entenc.h" |
|||
#include "entdec.h" |
|||
#include "rate.h" |
|||
|
|||
opus_int16 bitexact_cos(opus_int16 x); |
|||
int bitexact_log2tan(int isin,int icos); |
|||
|
|||
/** Compute the amplitude (sqrt energy) in each of the bands
|
|||
* @param m Mode data |
|||
* @param X Spectrum |
|||
* @param bandE Square root of the energy for each band (returned) |
|||
*/ |
|||
void compute_band_energies(const CELTMode *m, const celt_sig *X, celt_ener *bandE, int end, int C, int LM, int arch); |
|||
|
|||
/*void compute_noise_energies(const CELTMode *m, const celt_sig *X, const opus_val16 *tonality, celt_ener *bandE);*/ |
|||
|
|||
/** Normalise each band of X such that the energy in each band is
|
|||
equal to 1 |
|||
* @param m Mode data |
|||
* @param X Spectrum (returned normalised) |
|||
* @param bandE Square root of the energy for each band |
|||
*/ |
|||
void normalise_bands(const CELTMode *m, const celt_sig * OPUS_RESTRICT freq, celt_norm * OPUS_RESTRICT X, const celt_ener *bandE, int end, int C, int M); |
|||
|
|||
/** Denormalise each band of X to restore full amplitude
|
|||
* @param m Mode data |
|||
* @param X Spectrum (returned de-normalised) |
|||
* @param bandE Square root of the energy for each band |
|||
*/ |
|||
void denormalise_bands(const CELTMode *m, const celt_norm * OPUS_RESTRICT X, |
|||
celt_sig * OPUS_RESTRICT freq, const opus_val16 *bandE, int start, |
|||
int end, int M, int downsample, int silence); |
|||
|
|||
#define SPREAD_NONE (0) |
|||
#define SPREAD_LIGHT (1) |
|||
#define SPREAD_NORMAL (2) |
|||
#define SPREAD_AGGRESSIVE (3) |
|||
|
|||
int spreading_decision(const CELTMode *m, const celt_norm *X, int *average, |
|||
int last_decision, int *hf_average, int *tapset_decision, int update_hf, |
|||
int end, int C, int M, const int *spread_weight); |
|||
|
|||
#ifdef MEASURE_NORM_MSE |
|||
void measure_norm_mse(const CELTMode *m, float *X, float *X0, float *bandE, float *bandE0, int M, int N, int C); |
|||
#endif |
|||
|
|||
void haar1(celt_norm *X, int N0, int stride); |
|||
|
|||
/** Quantisation/encoding of the residual spectrum
|
|||
* @param encode flag that indicates whether we're encoding (1) or decoding (0) |
|||
* @param m Mode data |
|||
* @param start First band to process |
|||
* @param end Last band to process + 1 |
|||
* @param X Residual (normalised) |
|||
* @param Y Residual (normalised) for second channel (or NULL for mono) |
|||
* @param collapse_masks Anti-collapse tracking mask |
|||
* @param bandE Square root of the energy for each band |
|||
* @param pulses Bit allocation (per band) for PVQ |
|||
* @param shortBlocks Zero for long blocks, non-zero for short blocks |
|||
* @param spread Amount of spreading to use |
|||
* @param dual_stereo Zero for MS stereo, non-zero for dual stereo |
|||
* @param intensity First band to use intensity stereo |
|||
* @param tf_res Time-frequency resolution change |
|||
* @param total_bits Total number of bits that can be used for the frame (including the ones already spent) |
|||
* @param balance Number of unallocated bits |
|||
* @param en Entropy coder state |
|||
* @param LM log2() of the number of 2.5 subframes in the frame |
|||
* @param codedBands Last band to receive bits + 1 |
|||
* @param seed Random generator seed |
|||
* @param arch Run-time architecture (see opus_select_arch()) |
|||
*/ |
|||
void quant_all_bands(int encode, const CELTMode *m, int start, int end, |
|||
celt_norm * X, celt_norm * Y, unsigned char *collapse_masks, |
|||
const celt_ener *bandE, int *pulses, int shortBlocks, int spread, |
|||
int dual_stereo, int intensity, int *tf_res, opus_int32 total_bits, |
|||
opus_int32 balance, ec_ctx *ec, int M, int codedBands, opus_uint32 *seed, |
|||
int complexity, int arch, int disable_inv); |
|||
|
|||
void anti_collapse(const CELTMode *m, celt_norm *X_, |
|||
unsigned char *collapse_masks, int LM, int C, int size, int start, |
|||
int end, const opus_val16 *logE, const opus_val16 *prev1logE, |
|||
const opus_val16 *prev2logE, const int *pulses, opus_uint32 seed, |
|||
int arch); |
|||
|
|||
opus_uint32 celt_lcg_rand(opus_uint32 seed); |
|||
|
|||
int hysteresis_decision(opus_val16 val, const opus_val16 *thresholds, const opus_val16 *hysteresis, int N, int prev); |
|||
|
|||
#endif /* BANDS_H */ |
|||
@ -0,0 +1,316 @@ |
|||
/* Copyright (c) 2007-2008 CSIRO
|
|||
Copyright (c) 2007-2010 Xiph.Org Foundation |
|||
Copyright (c) 2008 Gregory Maxwell |
|||
Written by Jean-Marc Valin and Gregory Maxwell */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#define CELT_C |
|||
|
|||
#include "os_support.h" |
|||
#include "mdct.h" |
|||
#include <math.h> |
|||
#include "celt.h" |
|||
#include "pitch.h" |
|||
#include "bands.h" |
|||
#include "modes.h" |
|||
#include "entcode.h" |
|||
#include "quant_bands.h" |
|||
#include "rate.h" |
|||
#include "stack_alloc.h" |
|||
#include "mathops.h" |
|||
#include "float_cast.h" |
|||
#include <stdarg.h> |
|||
#include "celt_lpc.h" |
|||
#include "vq.h" |
|||
|
|||
#ifndef PACKAGE_VERSION |
|||
#define PACKAGE_VERSION "unknown" |
|||
#endif |
|||
|
|||
#if defined(MIPSr1_ASM) |
|||
#include "mips/celt_mipsr1.h" |
|||
#endif |
|||
|
|||
|
|||
int resampling_factor(opus_int32 rate) |
|||
{ |
|||
int ret; |
|||
switch (rate) |
|||
{ |
|||
case 48000: |
|||
ret = 1; |
|||
break; |
|||
case 24000: |
|||
ret = 2; |
|||
break; |
|||
case 16000: |
|||
ret = 3; |
|||
break; |
|||
case 12000: |
|||
ret = 4; |
|||
break; |
|||
case 8000: |
|||
ret = 6; |
|||
break; |
|||
default: |
|||
#ifndef CUSTOM_MODES |
|||
celt_assert(0); |
|||
#endif |
|||
ret = 0; |
|||
break; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
#if !defined(OVERRIDE_COMB_FILTER_CONST) || defined(NON_STATIC_COMB_FILTER_CONST_C) |
|||
/* This version should be faster on ARM */ |
|||
#ifdef OPUS_ARM_ASM |
|||
#ifndef NON_STATIC_COMB_FILTER_CONST_C |
|||
static |
|||
#endif |
|||
void comb_filter_const_c(opus_val32 *y, opus_val32 *x, int T, int N, |
|||
opus_val16 g10, opus_val16 g11, opus_val16 g12) |
|||
{ |
|||
opus_val32 x0, x1, x2, x3, x4; |
|||
int i; |
|||
x4 = SHL32(x[-T-2], 1); |
|||
x3 = SHL32(x[-T-1], 1); |
|||
x2 = SHL32(x[-T], 1); |
|||
x1 = SHL32(x[-T+1], 1); |
|||
for (i=0;i<N-4;i+=5) |
|||
{ |
|||
opus_val32 t; |
|||
x0=SHL32(x[i-T+2],1); |
|||
t = MAC16_32_Q16(x[i], g10, x2); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x1,x3)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x0,x4)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i] = t; |
|||
x4=SHL32(x[i-T+3],1); |
|||
t = MAC16_32_Q16(x[i+1], g10, x1); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x0,x2)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x4,x3)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i+1] = t; |
|||
x3=SHL32(x[i-T+4],1); |
|||
t = MAC16_32_Q16(x[i+2], g10, x0); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x4,x1)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x3,x2)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i+2] = t; |
|||
x2=SHL32(x[i-T+5],1); |
|||
t = MAC16_32_Q16(x[i+3], g10, x4); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x3,x0)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x2,x1)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i+3] = t; |
|||
x1=SHL32(x[i-T+6],1); |
|||
t = MAC16_32_Q16(x[i+4], g10, x3); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x2,x4)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x1,x0)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i+4] = t; |
|||
} |
|||
#ifdef CUSTOM_MODES |
|||
for (;i<N;i++) |
|||
{ |
|||
opus_val32 t; |
|||
x0=SHL32(x[i-T+2],1); |
|||
t = MAC16_32_Q16(x[i], g10, x2); |
|||
t = MAC16_32_Q16(t, g11, ADD32(x1,x3)); |
|||
t = MAC16_32_Q16(t, g12, ADD32(x0,x4)); |
|||
t = SATURATE(t, SIG_SAT); |
|||
y[i] = t; |
|||
x4=x3; |
|||
x3=x2; |
|||
x2=x1; |
|||
x1=x0; |
|||
} |
|||
#endif |
|||
} |
|||
#else |
|||
#ifndef NON_STATIC_COMB_FILTER_CONST_C |
|||
static |
|||
#endif |
|||
void comb_filter_const_c(opus_val32 *y, opus_val32 *x, int T, int N, |
|||
opus_val16 g10, opus_val16 g11, opus_val16 g12) |
|||
{ |
|||
opus_val32 x0, x1, x2, x3, x4; |
|||
int i; |
|||
x4 = x[-T-2]; |
|||
x3 = x[-T-1]; |
|||
x2 = x[-T]; |
|||
x1 = x[-T+1]; |
|||
for (i=0;i<N;i++) |
|||
{ |
|||
x0=x[i-T+2]; |
|||
y[i] = x[i] |
|||
+ MULT16_32_Q15(g10,x2) |
|||
+ MULT16_32_Q15(g11,ADD32(x1,x3)) |
|||
+ MULT16_32_Q15(g12,ADD32(x0,x4)); |
|||
y[i] = SATURATE(y[i], SIG_SAT); |
|||
x4=x3; |
|||
x3=x2; |
|||
x2=x1; |
|||
x1=x0; |
|||
} |
|||
|
|||
} |
|||
#endif |
|||
#endif |
|||
|
|||
#ifndef OVERRIDE_comb_filter |
|||
void comb_filter(opus_val32 *y, opus_val32 *x, int T0, int T1, int N, |
|||
opus_val16 g0, opus_val16 g1, int tapset0, int tapset1, |
|||
const opus_val16 *window, int overlap, int arch) |
|||
{ |
|||
int i; |
|||
/* printf ("%d %d %f %f\n", T0, T1, g0, g1); */ |
|||
opus_val16 g00, g01, g02, g10, g11, g12; |
|||
opus_val32 x0, x1, x2, x3, x4; |
|||
static const opus_val16 gains[3][3] = { |
|||
{QCONST16(0.3066406250f, 15), QCONST16(0.2170410156f, 15), QCONST16(0.1296386719f, 15)}, |
|||
{QCONST16(0.4638671875f, 15), QCONST16(0.2680664062f, 15), QCONST16(0.f, 15)}, |
|||
{QCONST16(0.7998046875f, 15), QCONST16(0.1000976562f, 15), QCONST16(0.f, 15)}}; |
|||
|
|||
if (g0==0 && g1==0) |
|||
{ |
|||
/* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ |
|||
if (x!=y) |
|||
OPUS_MOVE(y, x, N); |
|||
return; |
|||
} |
|||
/* When the gain is zero, T0 and/or T1 is set to zero. We need
|
|||
to have then be at least 2 to avoid processing garbage data. */ |
|||
T0 = IMAX(T0, COMBFILTER_MINPERIOD); |
|||
T1 = IMAX(T1, COMBFILTER_MINPERIOD); |
|||
g00 = MULT16_16_P15(g0, gains[tapset0][0]); |
|||
g01 = MULT16_16_P15(g0, gains[tapset0][1]); |
|||
g02 = MULT16_16_P15(g0, gains[tapset0][2]); |
|||
g10 = MULT16_16_P15(g1, gains[tapset1][0]); |
|||
g11 = MULT16_16_P15(g1, gains[tapset1][1]); |
|||
g12 = MULT16_16_P15(g1, gains[tapset1][2]); |
|||
x1 = x[-T1+1]; |
|||
x2 = x[-T1 ]; |
|||
x3 = x[-T1-1]; |
|||
x4 = x[-T1-2]; |
|||
/* If the filter didn't change, we don't need the overlap */ |
|||
if (g0==g1 && T0==T1 && tapset0==tapset1) |
|||
overlap=0; |
|||
for (i=0;i<overlap;i++) |
|||
{ |
|||
opus_val16 f; |
|||
x0=x[i-T1+2]; |
|||
f = MULT16_16_Q15(window[i],window[i]); |
|||
y[i] = x[i] |
|||
+ MULT16_32_Q15(MULT16_16_Q15((Q15ONE-f),g00),x[i-T0]) |
|||
+ MULT16_32_Q15(MULT16_16_Q15((Q15ONE-f),g01),ADD32(x[i-T0+1],x[i-T0-1])) |
|||
+ MULT16_32_Q15(MULT16_16_Q15((Q15ONE-f),g02),ADD32(x[i-T0+2],x[i-T0-2])) |
|||
+ MULT16_32_Q15(MULT16_16_Q15(f,g10),x2) |
|||
+ MULT16_32_Q15(MULT16_16_Q15(f,g11),ADD32(x1,x3)) |
|||
+ MULT16_32_Q15(MULT16_16_Q15(f,g12),ADD32(x0,x4)); |
|||
y[i] = SATURATE(y[i], SIG_SAT); |
|||
x4=x3; |
|||
x3=x2; |
|||
x2=x1; |
|||
x1=x0; |
|||
|
|||
} |
|||
if (g1==0) |
|||
{ |
|||
/* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ |
|||
if (x!=y) |
|||
OPUS_MOVE(y+overlap, x+overlap, N-overlap); |
|||
return; |
|||
} |
|||
|
|||
/* Compute the part with the constant filter. */ |
|||
comb_filter_const(y+i, x+i, T1, N-i, g10, g11, g12, arch); |
|||
} |
|||
#endif /* OVERRIDE_comb_filter */ |
|||
|
|||
/* TF change table. Positive values mean better frequency resolution (longer
|
|||
effective window), whereas negative values mean better time resolution |
|||
(shorter effective window). The second index is computed as: |
|||
4*isTransient + 2*tf_select + per_band_flag */ |
|||
const signed char tf_select_table[4][8] = { |
|||
/*isTransient=0 isTransient=1 */ |
|||
{0, -1, 0, -1, 0,-1, 0,-1}, /* 2.5 ms */ |
|||
{0, -1, 0, -2, 1, 0, 1,-1}, /* 5 ms */ |
|||
{0, -2, 0, -3, 2, 0, 1,-1}, /* 10 ms */ |
|||
{0, -2, 0, -3, 3, 0, 1,-1}, /* 20 ms */ |
|||
}; |
|||
|
|||
|
|||
void init_caps(const CELTMode *m,int *cap,int LM,int C) |
|||
{ |
|||
int i; |
|||
for (i=0;i<m->nbEBands;i++) |
|||
{ |
|||
int N; |
|||
N=(m->eBands[i+1]-m->eBands[i])<<LM; |
|||
cap[i] = (m->cache.caps[m->nbEBands*(2*LM+C-1)+i]+64)*C*N>>2; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
const char *opus_strerror(int error) |
|||
{ |
|||
static const char * const error_strings[8] = { |
|||
"success", |
|||
"invalid argument", |
|||
"buffer too small", |
|||
"internal error", |
|||
"corrupted stream", |
|||
"request not implemented", |
|||
"invalid state", |
|||
"memory allocation failed" |
|||
}; |
|||
if (error > 0 || error < -7) |
|||
return "unknown error"; |
|||
else |
|||
return error_strings[-error]; |
|||
} |
|||
|
|||
const char *opus_get_version_string(void) |
|||
{ |
|||
return "libopus " PACKAGE_VERSION |
|||
/* Applications may rely on the presence of this substring in the version
|
|||
string to determine if they have a fixed-point or floating-point build |
|||
at runtime. */ |
|||
#ifdef FIXED_POINT |
|||
"-fixed" |
|||
#endif |
|||
#ifdef FUZZING |
|||
"-fuzzing" |
|||
#endif |
|||
; |
|||
} |
|||
@ -0,0 +1,252 @@ |
|||
/* Copyright (c) 2007-2008 CSIRO
|
|||
Copyright (c) 2007-2009 Xiph.Org Foundation |
|||
Copyright (c) 2008 Gregory Maxwell |
|||
Written by Jean-Marc Valin and Gregory Maxwell */ |
|||
/**
|
|||
@file celt.h |
|||
@brief Contains all the functions for encoding and decoding audio |
|||
*/ |
|||
|
|||
/*
|
|||
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 CELT_H |
|||
#define CELT_H |
|||
|
|||
#include "opus_types.h" |
|||
#include "opus_defines.h" |
|||
#include "opus_custom.h" |
|||
#include "entenc.h" |
|||
#include "entdec.h" |
|||
#include "arch.h" |
|||
|
|||
#ifdef ENABLE_DEEP_PLC |
|||
#include "lpcnet.h" |
|||
#endif |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
#define CELTEncoder OpusCustomEncoder |
|||
#define CELTDecoder OpusCustomDecoder |
|||
#define CELTMode OpusCustomMode |
|||
|
|||
#define LEAK_BANDS 19 |
|||
|
|||
typedef struct { |
|||
int valid; |
|||
float tonality; |
|||
float tonality_slope; |
|||
float noisiness; |
|||
float activity; |
|||
float music_prob; |
|||
float music_prob_min; |
|||
float music_prob_max; |
|||
int bandwidth; |
|||
float activity_probability; |
|||
float max_pitch_ratio; |
|||
/* Store as Q6 char to save space. */ |
|||
unsigned char leak_boost[LEAK_BANDS]; |
|||
} AnalysisInfo; |
|||
|
|||
typedef struct { |
|||
int signalType; |
|||
int offset; |
|||
} SILKInfo; |
|||
|
|||
#define __celt_check_mode_ptr_ptr(ptr) ((ptr) + ((ptr) - (const CELTMode**)(ptr))) |
|||
|
|||
#define __celt_check_analysis_ptr(ptr) ((ptr) + ((ptr) - (const AnalysisInfo*)(ptr))) |
|||
|
|||
#define __celt_check_silkinfo_ptr(ptr) ((ptr) + ((ptr) - (const SILKInfo*)(ptr))) |
|||
|
|||
/* Encoder/decoder Requests */ |
|||
|
|||
|
|||
#define CELT_SET_PREDICTION_REQUEST 10002 |
|||
/** Controls the use of interframe prediction.
|
|||
0=Independent frames |
|||
1=Short term interframe prediction allowed |
|||
2=Long term prediction allowed |
|||
*/ |
|||
#define CELT_SET_PREDICTION(x) CELT_SET_PREDICTION_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_SET_INPUT_CLIPPING_REQUEST 10004 |
|||
#define CELT_SET_INPUT_CLIPPING(x) CELT_SET_INPUT_CLIPPING_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_GET_AND_CLEAR_ERROR_REQUEST 10007 |
|||
#define CELT_GET_AND_CLEAR_ERROR(x) CELT_GET_AND_CLEAR_ERROR_REQUEST, __opus_check_int_ptr(x) |
|||
|
|||
#define CELT_SET_CHANNELS_REQUEST 10008 |
|||
#define CELT_SET_CHANNELS(x) CELT_SET_CHANNELS_REQUEST, __opus_check_int(x) |
|||
|
|||
|
|||
/* Internal */ |
|||
#define CELT_SET_START_BAND_REQUEST 10010 |
|||
#define CELT_SET_START_BAND(x) CELT_SET_START_BAND_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_SET_END_BAND_REQUEST 10012 |
|||
#define CELT_SET_END_BAND(x) CELT_SET_END_BAND_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_GET_MODE_REQUEST 10015 |
|||
/** Get the CELTMode used by an encoder or decoder */ |
|||
#define CELT_GET_MODE(x) CELT_GET_MODE_REQUEST, __celt_check_mode_ptr_ptr(x) |
|||
|
|||
#define CELT_SET_SIGNALLING_REQUEST 10016 |
|||
#define CELT_SET_SIGNALLING(x) CELT_SET_SIGNALLING_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_SET_TONALITY_REQUEST 10018 |
|||
#define CELT_SET_TONALITY(x) CELT_SET_TONALITY_REQUEST, __opus_check_int(x) |
|||
#define CELT_SET_TONALITY_SLOPE_REQUEST 10020 |
|||
#define CELT_SET_TONALITY_SLOPE(x) CELT_SET_TONALITY_SLOPE_REQUEST, __opus_check_int(x) |
|||
|
|||
#define CELT_SET_ANALYSIS_REQUEST 10022 |
|||
#define CELT_SET_ANALYSIS(x) CELT_SET_ANALYSIS_REQUEST, __celt_check_analysis_ptr(x) |
|||
|
|||
#define OPUS_SET_LFE_REQUEST 10024 |
|||
#define OPUS_SET_LFE(x) OPUS_SET_LFE_REQUEST, __opus_check_int(x) |
|||
|
|||
#define OPUS_SET_ENERGY_MASK_REQUEST 10026 |
|||
#define OPUS_SET_ENERGY_MASK(x) OPUS_SET_ENERGY_MASK_REQUEST, __opus_check_val16_ptr(x) |
|||
|
|||
#define CELT_SET_SILK_INFO_REQUEST 10028 |
|||
#define CELT_SET_SILK_INFO(x) CELT_SET_SILK_INFO_REQUEST, __celt_check_silkinfo_ptr(x) |
|||
|
|||
/* Encoder stuff */ |
|||
|
|||
int celt_encoder_get_size(int channels); |
|||
|
|||
int celt_encode_with_ec(OpusCustomEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc); |
|||
|
|||
int celt_encoder_init(CELTEncoder *st, opus_int32 sampling_rate, int channels, |
|||
int arch); |
|||
|
|||
|
|||
|
|||
/* Decoder stuff */ |
|||
|
|||
int celt_decoder_get_size(int channels); |
|||
|
|||
|
|||
int celt_decoder_init(CELTDecoder *st, opus_int32 sampling_rate, int channels); |
|||
|
|||
int celt_decode_with_ec_dred(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, |
|||
int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum |
|||
#ifdef ENABLE_DEEP_PLC |
|||
,LPCNetPLCState *lpcnet |
|||
#endif |
|||
); |
|||
|
|||
int celt_decode_with_ec(OpusCustomDecoder * OPUS_RESTRICT st, const unsigned char *data, |
|||
int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum); |
|||
|
|||
#define celt_encoder_ctl opus_custom_encoder_ctl |
|||
#define celt_decoder_ctl opus_custom_decoder_ctl |
|||
|
|||
|
|||
#ifdef CUSTOM_MODES |
|||
#define OPUS_CUSTOM_NOSTATIC |
|||
#else |
|||
#define OPUS_CUSTOM_NOSTATIC static OPUS_INLINE |
|||
#endif |
|||
|
|||
static const unsigned char trim_icdf[11] = {126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0}; |
|||
/* Probs: NONE: 21.875%, LIGHT: 6.25%, NORMAL: 65.625%, AGGRESSIVE: 6.25% */ |
|||
static const unsigned char spread_icdf[4] = {25, 23, 2, 0}; |
|||
|
|||
static const unsigned char tapset_icdf[3]={2,1,0}; |
|||
|
|||
#ifdef CUSTOM_MODES |
|||
static const unsigned char toOpusTable[20] = { |
|||
0xE0, 0xE8, 0xF0, 0xF8, |
|||
0xC0, 0xC8, 0xD0, 0xD8, |
|||
0xA0, 0xA8, 0xB0, 0xB8, |
|||
0x00, 0x00, 0x00, 0x00, |
|||
0x80, 0x88, 0x90, 0x98, |
|||
}; |
|||
|
|||
static const unsigned char fromOpusTable[16] = { |
|||
0x80, 0x88, 0x90, 0x98, |
|||
0x40, 0x48, 0x50, 0x58, |
|||
0x20, 0x28, 0x30, 0x38, |
|||
0x00, 0x08, 0x10, 0x18 |
|||
}; |
|||
|
|||
static OPUS_INLINE int toOpus(unsigned char c) |
|||
{ |
|||
int ret=0; |
|||
if (c<0xA0) |
|||
ret = toOpusTable[c>>3]; |
|||
if (ret == 0) |
|||
return -1; |
|||
else |
|||
return ret|(c&0x7); |
|||
} |
|||
|
|||
static OPUS_INLINE int fromOpus(unsigned char c) |
|||
{ |
|||
if (c<0x80) |
|||
return -1; |
|||
else |
|||
return fromOpusTable[(c>>3)-16] | (c&0x7); |
|||
} |
|||
#endif /* CUSTOM_MODES */ |
|||
|
|||
#define COMBFILTER_MAXPERIOD 1024 |
|||
#define COMBFILTER_MINPERIOD 15 |
|||
|
|||
extern const signed char tf_select_table[4][8]; |
|||
|
|||
#if defined(ENABLE_HARDENING) || defined(ENABLE_ASSERTIONS) |
|||
void validate_celt_decoder(CELTDecoder *st); |
|||
#define VALIDATE_CELT_DECODER(st) validate_celt_decoder(st) |
|||
#else |
|||
#define VALIDATE_CELT_DECODER(st) |
|||
#endif |
|||
|
|||
int resampling_factor(opus_int32 rate); |
|||
|
|||
void celt_preemphasis(const opus_val16 * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp, |
|||
int N, int CC, int upsample, const opus_val16 *coef, celt_sig *mem, int clip); |
|||
|
|||
void comb_filter(opus_val32 *y, opus_val32 *x, int T0, int T1, int N, |
|||
opus_val16 g0, opus_val16 g1, int tapset0, int tapset1, |
|||
const opus_val16 *window, int overlap, int arch); |
|||
|
|||
void init_caps(const CELTMode *m,int *cap,int LM,int C); |
|||
|
|||
#ifdef RESYNTH |
|||
void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, const opus_val16 *coef, celt_sig *mem, int accum); |
|||
void celt_synthesis(const CELTMode *mode, celt_norm *X, celt_sig * out_syn[], |
|||
opus_val16 *oldBandE, int start, int effEnd, int C, int CC, int isTransient, |
|||
int LM, int downsample, int silence, int arch); |
|||
#endif |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
#endif /* CELT_H */ |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,363 @@ |
|||
/* Copyright (c) 2009-2010 Xiph.Org Foundation
|
|||
Written by Jean-Marc Valin */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#include "celt_lpc.h" |
|||
#include "stack_alloc.h" |
|||
#include "mathops.h" |
|||
#include "pitch.h" |
|||
|
|||
void _celt_lpc( |
|||
opus_val16 *_lpc, /* out: [0...p-1] LPC coefficients */ |
|||
const opus_val32 *ac, /* in: [0...p] autocorrelation values */ |
|||
int p |
|||
) |
|||
{ |
|||
int i, j; |
|||
opus_val32 r; |
|||
opus_val32 error = ac[0]; |
|||
#ifdef FIXED_POINT |
|||
opus_val32 lpc[CELT_LPC_ORDER]; |
|||
#else |
|||
float *lpc = _lpc; |
|||
#endif |
|||
|
|||
OPUS_CLEAR(lpc, p); |
|||
#ifdef FIXED_POINT |
|||
if (ac[0] != 0) |
|||
#else |
|||
if (ac[0] > 1e-10f) |
|||
#endif |
|||
{ |
|||
for (i = 0; i < p; i++) { |
|||
/* Sum up this iteration's reflection coefficient */ |
|||
opus_val32 rr = 0; |
|||
for (j = 0; j < i; j++) |
|||
rr += MULT32_32_Q31(lpc[j],ac[i - j]); |
|||
rr += SHR32(ac[i + 1],6); |
|||
r = -frac_div32(SHL32(rr,6), error); |
|||
/* Update LPC coefficients and total error */ |
|||
lpc[i] = SHR32(r,6); |
|||
for (j = 0; j < (i+1)>>1; j++) |
|||
{ |
|||
opus_val32 tmp1, tmp2; |
|||
tmp1 = lpc[j]; |
|||
tmp2 = lpc[i-1-j]; |
|||
lpc[j] = tmp1 + MULT32_32_Q31(r,tmp2); |
|||
lpc[i-1-j] = tmp2 + MULT32_32_Q31(r,tmp1); |
|||
} |
|||
|
|||
error = error - MULT32_32_Q31(MULT32_32_Q31(r,r),error); |
|||
/* Bail out once we get 30 dB gain */ |
|||
#ifdef FIXED_POINT |
|||
if (error<=SHR32(ac[0],10)) |
|||
break; |
|||
#else |
|||
if (error<=.001f*ac[0]) |
|||
break; |
|||
#endif |
|||
} |
|||
} |
|||
#ifdef FIXED_POINT |
|||
{ |
|||
/* Convert the int32 lpcs to int16 and ensure there are no wrap-arounds.
|
|||
This reuses the logic in silk_LPC_fit() and silk_bwexpander_32(). Any bug |
|||
fixes should also be applied there. */ |
|||
int iter, idx = 0; |
|||
opus_val32 maxabs, absval, chirp_Q16, chirp_minus_one_Q16; |
|||
|
|||
for (iter = 0; iter < 10; iter++) { |
|||
maxabs = 0; |
|||
for (i = 0; i < p; i++) { |
|||
absval = ABS32(lpc[i]); |
|||
if (absval > maxabs) { |
|||
maxabs = absval; |
|||
idx = i; |
|||
} |
|||
} |
|||
maxabs = PSHR32(maxabs, 13); /* Q25->Q12 */ |
|||
|
|||
if (maxabs > 32767) { |
|||
maxabs = MIN32(maxabs, 163838); |
|||
chirp_Q16 = QCONST32(0.999, 16) - DIV32(SHL32(maxabs - 32767, 14), |
|||
SHR32(MULT32_32_32(maxabs, idx + 1), 2)); |
|||
chirp_minus_one_Q16 = chirp_Q16 - 65536; |
|||
|
|||
/* Apply bandwidth expansion. */ |
|||
for (i = 0; i < p - 1; i++) { |
|||
lpc[i] = MULT32_32_Q16(chirp_Q16, lpc[i]); |
|||
chirp_Q16 += PSHR32(MULT32_32_32(chirp_Q16, chirp_minus_one_Q16), 16); |
|||
} |
|||
lpc[p - 1] = MULT32_32_Q16(chirp_Q16, lpc[p - 1]); |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (iter == 10) { |
|||
/* If the coeffs still do not fit into the 16 bit range after 10 iterations,
|
|||
fall back to the A(z)=1 filter. */ |
|||
OPUS_CLEAR(lpc, p); |
|||
_lpc[0] = 4096; /* Q12 */ |
|||
} else { |
|||
for (i = 0; i < p; i++) { |
|||
_lpc[i] = EXTRACT16(PSHR32(lpc[i], 13)); /* Q25->Q12 */ |
|||
} |
|||
} |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
void celt_fir_c( |
|||
const opus_val16 *x, |
|||
const opus_val16 *num, |
|||
opus_val16 *y, |
|||
int N, |
|||
int ord, |
|||
int arch) |
|||
{ |
|||
int i,j; |
|||
VARDECL(opus_val16, rnum); |
|||
SAVE_STACK; |
|||
celt_assert(x != y); |
|||
ALLOC(rnum, ord, opus_val16); |
|||
for(i=0;i<ord;i++) |
|||
rnum[i] = num[ord-i-1]; |
|||
for (i=0;i<N-3;i+=4) |
|||
{ |
|||
opus_val32 sum[4]; |
|||
sum[0] = SHL32(EXTEND32(x[i ]), SIG_SHIFT); |
|||
sum[1] = SHL32(EXTEND32(x[i+1]), SIG_SHIFT); |
|||
sum[2] = SHL32(EXTEND32(x[i+2]), SIG_SHIFT); |
|||
sum[3] = SHL32(EXTEND32(x[i+3]), SIG_SHIFT); |
|||
#if defined(OPUS_CHECK_ASM) && defined(FIXED_POINT) |
|||
{ |
|||
opus_val32 sum_c[4]; |
|||
memcpy(sum_c, sum, sizeof(sum_c)); |
|||
xcorr_kernel_c(rnum, x+i-ord, sum_c, ord); |
|||
#endif |
|||
xcorr_kernel(rnum, x+i-ord, sum, ord, arch); |
|||
#if defined(OPUS_CHECK_ASM) && defined(FIXED_POINT) |
|||
celt_assert(memcmp(sum, sum_c, sizeof(sum)) == 0); |
|||
} |
|||
#endif |
|||
y[i ] = SROUND16(sum[0], SIG_SHIFT); |
|||
y[i+1] = SROUND16(sum[1], SIG_SHIFT); |
|||
y[i+2] = SROUND16(sum[2], SIG_SHIFT); |
|||
y[i+3] = SROUND16(sum[3], SIG_SHIFT); |
|||
} |
|||
for (;i<N;i++) |
|||
{ |
|||
opus_val32 sum = SHL32(EXTEND32(x[i]), SIG_SHIFT); |
|||
for (j=0;j<ord;j++) |
|||
sum = MAC16_16(sum,rnum[j],x[i+j-ord]); |
|||
y[i] = SROUND16(sum, SIG_SHIFT); |
|||
} |
|||
RESTORE_STACK; |
|||
} |
|||
|
|||
void celt_iir(const opus_val32 *_x, |
|||
const opus_val16 *den, |
|||
opus_val32 *_y, |
|||
int N, |
|||
int ord, |
|||
opus_val16 *mem, |
|||
int arch) |
|||
{ |
|||
#ifdef SMALL_FOOTPRINT |
|||
int i,j; |
|||
(void)arch; |
|||
for (i=0;i<N;i++) |
|||
{ |
|||
opus_val32 sum = _x[i]; |
|||
for (j=0;j<ord;j++) |
|||
{ |
|||
sum -= MULT16_16(den[j],mem[j]); |
|||
} |
|||
for (j=ord-1;j>=1;j--) |
|||
{ |
|||
mem[j]=mem[j-1]; |
|||
} |
|||
mem[0] = SROUND16(sum, SIG_SHIFT); |
|||
_y[i] = sum; |
|||
} |
|||
#else |
|||
int i,j; |
|||
VARDECL(opus_val16, rden); |
|||
VARDECL(opus_val16, y); |
|||
SAVE_STACK; |
|||
|
|||
celt_assert((ord&3)==0); |
|||
ALLOC(rden, ord, opus_val16); |
|||
ALLOC(y, N+ord, opus_val16); |
|||
for(i=0;i<ord;i++) |
|||
rden[i] = den[ord-i-1]; |
|||
for(i=0;i<ord;i++) |
|||
y[i] = -mem[ord-i-1]; |
|||
for(;i<N+ord;i++) |
|||
y[i]=0; |
|||
for (i=0;i<N-3;i+=4) |
|||
{ |
|||
/* Unroll by 4 as if it were an FIR filter */ |
|||
opus_val32 sum[4]; |
|||
sum[0]=_x[i]; |
|||
sum[1]=_x[i+1]; |
|||
sum[2]=_x[i+2]; |
|||
sum[3]=_x[i+3]; |
|||
#if defined(OPUS_CHECK_ASM) && defined(FIXED_POINT) |
|||
{ |
|||
opus_val32 sum_c[4]; |
|||
memcpy(sum_c, sum, sizeof(sum_c)); |
|||
xcorr_kernel_c(rden, y+i, sum_c, ord); |
|||
#endif |
|||
xcorr_kernel(rden, y+i, sum, ord, arch); |
|||
#if defined(OPUS_CHECK_ASM) && defined(FIXED_POINT) |
|||
celt_assert(memcmp(sum, sum_c, sizeof(sum)) == 0); |
|||
} |
|||
#endif |
|||
/* Patch up the result to compensate for the fact that this is an IIR */ |
|||
y[i+ord ] = -SROUND16(sum[0],SIG_SHIFT); |
|||
_y[i ] = sum[0]; |
|||
sum[1] = MAC16_16(sum[1], y[i+ord ], den[0]); |
|||
y[i+ord+1] = -SROUND16(sum[1],SIG_SHIFT); |
|||
_y[i+1] = sum[1]; |
|||
sum[2] = MAC16_16(sum[2], y[i+ord+1], den[0]); |
|||
sum[2] = MAC16_16(sum[2], y[i+ord ], den[1]); |
|||
y[i+ord+2] = -SROUND16(sum[2],SIG_SHIFT); |
|||
_y[i+2] = sum[2]; |
|||
|
|||
sum[3] = MAC16_16(sum[3], y[i+ord+2], den[0]); |
|||
sum[3] = MAC16_16(sum[3], y[i+ord+1], den[1]); |
|||
sum[3] = MAC16_16(sum[3], y[i+ord ], den[2]); |
|||
y[i+ord+3] = -SROUND16(sum[3],SIG_SHIFT); |
|||
_y[i+3] = sum[3]; |
|||
} |
|||
for (;i<N;i++) |
|||
{ |
|||
opus_val32 sum = _x[i]; |
|||
for (j=0;j<ord;j++) |
|||
sum -= MULT16_16(rden[j],y[i+j]); |
|||
y[i+ord] = SROUND16(sum,SIG_SHIFT); |
|||
_y[i] = sum; |
|||
} |
|||
for(i=0;i<ord;i++) |
|||
mem[i] = _y[N-i-1]; |
|||
RESTORE_STACK; |
|||
#endif |
|||
} |
|||
|
|||
int _celt_autocorr( |
|||
const opus_val16 *x, /* in: [0...n-1] samples x */ |
|||
opus_val32 *ac, /* out: [0...lag-1] ac values */ |
|||
const opus_val16 *window, |
|||
int overlap, |
|||
int lag, |
|||
int n, |
|||
int arch |
|||
) |
|||
{ |
|||
opus_val32 d; |
|||
int i, k; |
|||
int fastN=n-lag; |
|||
int shift; |
|||
const opus_val16 *xptr; |
|||
VARDECL(opus_val16, xx); |
|||
SAVE_STACK; |
|||
ALLOC(xx, n, opus_val16); |
|||
celt_assert(n>0); |
|||
celt_assert(overlap>=0); |
|||
if (overlap == 0) |
|||
{ |
|||
xptr = x; |
|||
} else { |
|||
for (i=0;i<n;i++) |
|||
xx[i] = x[i]; |
|||
for (i=0;i<overlap;i++) |
|||
{ |
|||
xx[i] = MULT16_16_Q15(x[i],window[i]); |
|||
xx[n-i-1] = MULT16_16_Q15(x[n-i-1],window[i]); |
|||
} |
|||
xptr = xx; |
|||
} |
|||
shift=0; |
|||
#ifdef FIXED_POINT |
|||
{ |
|||
opus_val32 ac0; |
|||
ac0 = 1+(n<<7); |
|||
if (n&1) ac0 += SHR32(MULT16_16(xptr[0],xptr[0]),9); |
|||
for(i=(n&1);i<n;i+=2) |
|||
{ |
|||
ac0 += SHR32(MULT16_16(xptr[i],xptr[i]),9); |
|||
ac0 += SHR32(MULT16_16(xptr[i+1],xptr[i+1]),9); |
|||
} |
|||
|
|||
shift = celt_ilog2(ac0)-30+10; |
|||
shift = (shift)/2; |
|||
if (shift>0) |
|||
{ |
|||
for(i=0;i<n;i++) |
|||
xx[i] = PSHR32(xptr[i], shift); |
|||
xptr = xx; |
|||
} else |
|||
shift = 0; |
|||
} |
|||
#endif |
|||
celt_pitch_xcorr(xptr, xptr, ac, fastN, lag+1, arch); |
|||
for (k=0;k<=lag;k++) |
|||
{ |
|||
for (i = k+fastN, d = 0; i < n; i++) |
|||
d = MAC16_16(d, xptr[i], xptr[i-k]); |
|||
ac[k] += d; |
|||
} |
|||
#ifdef FIXED_POINT |
|||
shift = 2*shift; |
|||
if (shift<=0) |
|||
ac[0] += SHL32((opus_int32)1, -shift); |
|||
if (ac[0] < 268435456) |
|||
{ |
|||
int shift2 = 29 - EC_ILOG(ac[0]); |
|||
for (i=0;i<=lag;i++) |
|||
ac[i] = SHL32(ac[i], shift2); |
|||
shift -= shift2; |
|||
} else if (ac[0] >= 536870912) |
|||
{ |
|||
int shift2=1; |
|||
if (ac[0] >= 1073741824) |
|||
shift2++; |
|||
for (i=0;i<=lag;i++) |
|||
ac[i] = SHR32(ac[i], shift2); |
|||
shift += shift2; |
|||
} |
|||
#endif |
|||
|
|||
RESTORE_STACK; |
|||
return shift; |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
/* Copyright (c) 2009-2010 Xiph.Org Foundation
|
|||
Written by Jean-Marc Valin */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef PLC_H |
|||
#define PLC_H |
|||
|
|||
#include "arch.h" |
|||
#include "cpu_support.h" |
|||
|
|||
#if defined(OPUS_X86_MAY_HAVE_SSE4_1) |
|||
#include "x86/celt_lpc_sse.h" |
|||
#endif |
|||
|
|||
#define CELT_LPC_ORDER 24 |
|||
|
|||
void _celt_lpc(opus_val16 *_lpc, const opus_val32 *ac, int p); |
|||
|
|||
void celt_fir_c( |
|||
const opus_val16 *x, |
|||
const opus_val16 *num, |
|||
opus_val16 *y, |
|||
int N, |
|||
int ord, |
|||
int arch); |
|||
|
|||
#if !defined(OVERRIDE_CELT_FIR) |
|||
#define celt_fir(x, num, y, N, ord, arch) \ |
|||
(celt_fir_c(x, num, y, N, ord, arch)) |
|||
#endif |
|||
|
|||
void celt_iir(const opus_val32 *x, |
|||
const opus_val16 *den, |
|||
opus_val32 *y, |
|||
int N, |
|||
int ord, |
|||
opus_val16 *mem, |
|||
int arch); |
|||
|
|||
int _celt_autocorr(const opus_val16 *x, opus_val32 *ac, |
|||
const opus_val16 *window, int overlap, int lag, int n, int arch); |
|||
|
|||
#endif /* PLC_H */ |
|||
@ -0,0 +1,72 @@ |
|||
/* 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. |
|||
*/ |
|||
|
|||
#ifndef CPU_SUPPORT_H |
|||
#define CPU_SUPPORT_H |
|||
|
|||
#include "opus_types.h" |
|||
#include "opus_defines.h" |
|||
|
|||
#if defined(OPUS_HAVE_RTCD) && \ |
|||
(defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR)) |
|||
#include "arm/armcpu.h" |
|||
|
|||
/* We currently support 5 ARM variants:
|
|||
* arch[0] -> ARMv4 |
|||
* arch[1] -> ARMv5E |
|||
* arch[2] -> ARMv6 |
|||
* arch[3] -> NEON |
|||
* arch[4] -> NEON+DOTPROD |
|||
*/ |
|||
#define OPUS_ARCHMASK 7 |
|||
|
|||
#elif defined(OPUS_HAVE_RTCD) && \ |
|||
((defined(OPUS_X86_MAY_HAVE_SSE) && !defined(OPUS_X86_PRESUME_SSE)) || \ |
|||
(defined(OPUS_X86_MAY_HAVE_SSE2) && !defined(OPUS_X86_PRESUME_SSE2)) || \ |
|||
(defined(OPUS_X86_MAY_HAVE_SSE4_1) && !defined(OPUS_X86_PRESUME_SSE4_1)) || \ |
|||
(defined(OPUS_X86_MAY_HAVE_AVX2) && !defined(OPUS_X86_PRESUME_AVX2))) |
|||
|
|||
#include "x86/x86cpu.h" |
|||
/* We currently support 5 x86 variants:
|
|||
* arch[0] -> non-sse |
|||
* arch[1] -> sse |
|||
* arch[2] -> sse2 |
|||
* arch[3] -> sse4.1 |
|||
* arch[4] -> avx |
|||
*/ |
|||
#define OPUS_ARCHMASK 7 |
|||
int opus_select_arch(void); |
|||
|
|||
#else |
|||
#define OPUS_ARCHMASK 0 |
|||
|
|||
static OPUS_INLINE int opus_select_arch(void) |
|||
{ |
|||
return 0; |
|||
} |
|||
#endif |
|||
#endif |
|||
@ -0,0 +1,715 @@ |
|||
/* Copyright (c) 2007-2008 CSIRO
|
|||
Copyright (c) 2007-2009 Xiph.Org Foundation |
|||
Copyright (c) 2007-2009 Timothy B. Terriberry |
|||
Written by Timothy B. Terriberry and Jean-Marc Valin */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#include "os_support.h" |
|||
#include "cwrs.h" |
|||
#include "mathops.h" |
|||
#include "arch.h" |
|||
|
|||
#ifdef CUSTOM_MODES |
|||
|
|||
/*Guaranteed to return a conservatively large estimate of the binary logarithm
|
|||
with frac bits of fractional precision. |
|||
Tested for all possible 32-bit inputs with frac=4, where the maximum |
|||
overestimation is 0.06254243 bits.*/ |
|||
int log2_frac(opus_uint32 val, int frac) |
|||
{ |
|||
int l; |
|||
l=EC_ILOG(val); |
|||
if(val&(val-1)){ |
|||
/*This is (val>>l-16), but guaranteed to round up, even if adding a bias
|
|||
before the shift would cause overflow (e.g., for 0xFFFFxxxx). |
|||
Doesn't work for val=0, but that case fails the test above.*/ |
|||
if(l>16)val=((val-1)>>(l-16))+1; |
|||
else val<<=16-l; |
|||
l=(l-1)<<frac; |
|||
/*Note that we always need one iteration, since the rounding up above means
|
|||
that we might need to adjust the integer part of the logarithm.*/ |
|||
do{ |
|||
int b; |
|||
b=(int)(val>>16); |
|||
l+=b<<frac; |
|||
val=(val+b)>>b; |
|||
val=(val*val+0x7FFF)>>15; |
|||
} |
|||
while(frac-->0); |
|||
/*If val is not exactly 0x8000, then we have to round up the remainder.*/ |
|||
return l+(val>0x8000); |
|||
} |
|||
/*Exact powers of two require no rounding.*/ |
|||
else return (l-1)<<frac; |
|||
} |
|||
#endif |
|||
|
|||
/*Although derived separately, the pulse vector coding scheme is equivalent to
|
|||
a Pyramid Vector Quantizer \cite{Fis86}. |
|||
Some additional notes about an early version appear at |
|||
https://people.xiph.org/~tterribe/notes/cwrs.html, but the codebook ordering
|
|||
and the definitions of some terms have evolved since that was written. |
|||
|
|||
The conversion from a pulse vector to an integer index (encoding) and back |
|||
(decoding) is governed by two related functions, V(N,K) and U(N,K). |
|||
|
|||
V(N,K) = the number of combinations, with replacement, of N items, taken K |
|||
at a time, when a sign bit is added to each item taken at least once (i.e., |
|||
the number of N-dimensional unit pulse vectors with K pulses). |
|||
One way to compute this is via |
|||
V(N,K) = K>0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1, |
|||
where choose() is the binomial function. |
|||
A table of values for N<10 and K<10 looks like: |
|||
V[10][10] = { |
|||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
|||
{1, 2, 2, 2, 2, 2, 2, 2, 2, 2}, |
|||
{1, 4, 8, 12, 16, 20, 24, 28, 32, 36}, |
|||
{1, 6, 18, 38, 66, 102, 146, 198, 258, 326}, |
|||
{1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992}, |
|||
{1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290}, |
|||
{1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436}, |
|||
{1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598}, |
|||
{1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688}, |
|||
{1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146} |
|||
}; |
|||
|
|||
U(N,K) = the number of such combinations wherein N-1 objects are taken at |
|||
most K-1 at a time. |
|||
This is given by |
|||
U(N,K) = sum(k=0...K-1,V(N-1,k)) |
|||
= K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0. |
|||
The latter expression also makes clear that U(N,K) is half the number of such |
|||
combinations wherein the first object is taken at least once. |
|||
Although it may not be clear from either of these definitions, U(N,K) is the |
|||
natural function to work with when enumerating the pulse vector codebooks, |
|||
not V(N,K). |
|||
U(N,K) is not well-defined for N=0, but with the extension |
|||
U(0,K) = K>0 ? 0 : 1, |
|||
the function becomes symmetric: U(N,K) = U(K,N), with a similar table: |
|||
U[10][10] = { |
|||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
|||
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, |
|||
{0, 1, 3, 5, 7, 9, 11, 13, 15, 17}, |
|||
{0, 1, 5, 13, 25, 41, 61, 85, 113, 145}, |
|||
{0, 1, 7, 25, 63, 129, 231, 377, 575, 833}, |
|||
{0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649}, |
|||
{0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073}, |
|||
{0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081}, |
|||
{0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545}, |
|||
{0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729} |
|||
}; |
|||
|
|||
With this extension, V(N,K) may be written in terms of U(N,K): |
|||
V(N,K) = U(N,K) + U(N,K+1) |
|||
for all N>=0, K>=0. |
|||
Thus U(N,K+1) represents the number of combinations where the first element |
|||
is positive or zero, and U(N,K) represents the number of combinations where |
|||
it is negative. |
|||
With a large enough table of U(N,K) values, we could write O(N) encoding |
|||
and O(min(N*log(K),N+K)) decoding routines, but such a table would be |
|||
prohibitively large for small embedded devices (K may be as large as 32767 |
|||
for small N, and N may be as large as 200). |
|||
|
|||
Both functions obey the same recurrence relation: |
|||
V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1), |
|||
U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1), |
|||
for all N>0, K>0, with different initial conditions at N=0 or K=0. |
|||
This allows us to construct a row of one of the tables above given the |
|||
previous row or the next row. |
|||
Thus we can derive O(NK) encoding and decoding routines with O(K) memory |
|||
using only addition and subtraction. |
|||
|
|||
When encoding, we build up from the U(2,K) row and work our way forwards. |
|||
When decoding, we need to start at the U(N,K) row and work our way backwards, |
|||
which requires a means of computing U(N,K). |
|||
U(N,K) may be computed from two previous values with the same N: |
|||
U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2) |
|||
for all N>1, and since U(N,K) is symmetric, a similar relation holds for two |
|||
previous values with the same K: |
|||
U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K) |
|||
for all K>1. |
|||
This allows us to construct an arbitrary row of the U(N,K) table by starting |
|||
with the first two values, which are constants. |
|||
This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K) |
|||
multiplications. |
|||
Similar relations can be derived for V(N,K), but are not used here. |
|||
|
|||
For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree |
|||
polynomial for fixed N. |
|||
The first few are |
|||
U(1,K) = 1, |
|||
U(2,K) = 2*K-1, |
|||
U(3,K) = (2*K-2)*K+1, |
|||
U(4,K) = (((4*K-6)*K+8)*K-3)/3, |
|||
U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3, |
|||
and |
|||
V(1,K) = 2, |
|||
V(2,K) = 4*K, |
|||
V(3,K) = 4*K*K+2, |
|||
V(4,K) = 8*(K*K+2)*K/3, |
|||
V(5,K) = ((4*K*K+20)*K*K+6)/3, |
|||
for all K>0. |
|||
This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for |
|||
small N (and indeed decoding is also O(N) for N<3). |
|||
|
|||
@ARTICLE{Fis86, |
|||
author="Thomas R. Fischer", |
|||
title="A Pyramid Vector Quantizer", |
|||
journal="IEEE Transactions on Information Theory", |
|||
volume="IT-32", |
|||
number=4, |
|||
pages="568--583", |
|||
month=Jul, |
|||
year=1986 |
|||
}*/ |
|||
|
|||
#if !defined(SMALL_FOOTPRINT) |
|||
|
|||
/*U(N,K) = U(K,N) := N>0?K>0?U(N-1,K)+U(N,K-1)+U(N-1,K-1):0:K>0?1:0*/ |
|||
# define CELT_PVQ_U(_n,_k) (CELT_PVQ_U_ROW[IMIN(_n,_k)][IMAX(_n,_k)]) |
|||
/*V(N,K) := U(N,K)+U(N,K+1) = the number of PVQ codewords for a band of size N
|
|||
with K pulses allocated to it.*/ |
|||
# define CELT_PVQ_V(_n,_k) (CELT_PVQ_U(_n,_k)+CELT_PVQ_U(_n,(_k)+1)) |
|||
|
|||
/*For each V(N,K) supported, we will access element U(min(N,K+1),max(N,K+1)).
|
|||
Thus, the number of entries in row I is the larger of the maximum number of |
|||
pulses we will ever allocate for a given N=I (K=128, or however many fit in |
|||
32 bits, whichever is smaller), plus one, and the maximum N for which |
|||
K=I-1 pulses fit in 32 bits. |
|||
The largest band size in an Opus Custom mode is 208. |
|||
Otherwise, we can limit things to the set of N which can be achieved by |
|||
splitting a band from a standard Opus mode: 176, 144, 96, 88, 72, 64, 48, |
|||
44, 36, 32, 24, 22, 18, 16, 8, 4, 2).*/ |
|||
#if defined(CUSTOM_MODES) |
|||
static const opus_uint32 CELT_PVQ_U_DATA[1488]={ |
|||
#else |
|||
static const opus_uint32 CELT_PVQ_U_DATA[1272]={ |
|||
#endif |
|||
/*N=0, K=0...176:*/ |
|||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|||
0, 0, 0, 0, 0, 0, |
|||
#endif |
|||
/*N=1, K=1...176:*/ |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|||
1, 1, 1, 1, 1, 1, |
|||
#endif |
|||
/*N=2, K=2...176:*/ |
|||
3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, |
|||
43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, |
|||
81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, |
|||
115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, |
|||
145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, |
|||
175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, |
|||
205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, |
|||
235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, |
|||
265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, |
|||
295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, |
|||
325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, |
|||
383, 385, 387, 389, 391, 393, 395, 397, 399, 401, 403, 405, 407, 409, 411, |
|||
413, 415, |
|||
#endif |
|||
/*N=3, K=3...176:*/ |
|||
13, 25, 41, 61, 85, 113, 145, 181, 221, 265, 313, 365, 421, 481, 545, 613, |
|||
685, 761, 841, 925, 1013, 1105, 1201, 1301, 1405, 1513, 1625, 1741, 1861, |
|||
1985, 2113, 2245, 2381, 2521, 2665, 2813, 2965, 3121, 3281, 3445, 3613, 3785, |
|||
3961, 4141, 4325, 4513, 4705, 4901, 5101, 5305, 5513, 5725, 5941, 6161, 6385, |
|||
6613, 6845, 7081, 7321, 7565, 7813, 8065, 8321, 8581, 8845, 9113, 9385, 9661, |
|||
9941, 10225, 10513, 10805, 11101, 11401, 11705, 12013, 12325, 12641, 12961, |
|||
13285, 13613, 13945, 14281, 14621, 14965, 15313, 15665, 16021, 16381, 16745, |
|||
17113, 17485, 17861, 18241, 18625, 19013, 19405, 19801, 20201, 20605, 21013, |
|||
21425, 21841, 22261, 22685, 23113, 23545, 23981, 24421, 24865, 25313, 25765, |
|||
26221, 26681, 27145, 27613, 28085, 28561, 29041, 29525, 30013, 30505, 31001, |
|||
31501, 32005, 32513, 33025, 33541, 34061, 34585, 35113, 35645, 36181, 36721, |
|||
37265, 37813, 38365, 38921, 39481, 40045, 40613, 41185, 41761, 42341, 42925, |
|||
43513, 44105, 44701, 45301, 45905, 46513, 47125, 47741, 48361, 48985, 49613, |
|||
50245, 50881, 51521, 52165, 52813, 53465, 54121, 54781, 55445, 56113, 56785, |
|||
57461, 58141, 58825, 59513, 60205, 60901, 61601, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
62305, 63013, 63725, 64441, 65161, 65885, 66613, 67345, 68081, 68821, 69565, |
|||
70313, 71065, 71821, 72581, 73345, 74113, 74885, 75661, 76441, 77225, 78013, |
|||
78805, 79601, 80401, 81205, 82013, 82825, 83641, 84461, 85285, 86113, |
|||
#endif |
|||
/*N=4, K=4...176:*/ |
|||
63, 129, 231, 377, 575, 833, 1159, 1561, 2047, 2625, 3303, 4089, 4991, 6017, |
|||
7175, 8473, 9919, 11521, 13287, 15225, 17343, 19649, 22151, 24857, 27775, |
|||
30913, 34279, 37881, 41727, 45825, 50183, 54809, 59711, 64897, 70375, 76153, |
|||
82239, 88641, 95367, 102425, 109823, 117569, 125671, 134137, 142975, 152193, |
|||
161799, 171801, 182207, 193025, 204263, 215929, 228031, 240577, 253575, |
|||
267033, 280959, 295361, 310247, 325625, 341503, 357889, 374791, 392217, |
|||
410175, 428673, 447719, 467321, 487487, 508225, 529543, 551449, 573951, |
|||
597057, 620775, 645113, 670079, 695681, 721927, 748825, 776383, 804609, |
|||
833511, 863097, 893375, 924353, 956039, 988441, 1021567, 1055425, 1090023, |
|||
1125369, 1161471, 1198337, 1235975, 1274393, 1313599, 1353601, 1394407, |
|||
1436025, 1478463, 1521729, 1565831, 1610777, 1656575, 1703233, 1750759, |
|||
1799161, 1848447, 1898625, 1949703, 2001689, 2054591, 2108417, 2163175, |
|||
2218873, 2275519, 2333121, 2391687, 2451225, 2511743, 2573249, 2635751, |
|||
2699257, 2763775, 2829313, 2895879, 2963481, 3032127, 3101825, 3172583, |
|||
3244409, 3317311, 3391297, 3466375, 3542553, 3619839, 3698241, 3777767, |
|||
3858425, 3940223, 4023169, 4107271, 4192537, 4278975, 4366593, 4455399, |
|||
4545401, 4636607, 4729025, 4822663, 4917529, 5013631, 5110977, 5209575, |
|||
5309433, 5410559, 5512961, 5616647, 5721625, 5827903, 5935489, 6044391, |
|||
6154617, 6266175, 6379073, 6493319, 6608921, 6725887, 6844225, 6963943, |
|||
7085049, 7207551, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
7331457, 7456775, 7583513, 7711679, 7841281, 7972327, 8104825, 8238783, |
|||
8374209, 8511111, 8649497, 8789375, 8930753, 9073639, 9218041, 9363967, |
|||
9511425, 9660423, 9810969, 9963071, 10116737, 10271975, 10428793, 10587199, |
|||
10747201, 10908807, 11072025, 11236863, 11403329, 11571431, 11741177, |
|||
11912575, |
|||
#endif |
|||
/*N=5, K=5...176:*/ |
|||
321, 681, 1289, 2241, 3649, 5641, 8361, 11969, 16641, 22569, 29961, 39041, |
|||
50049, 63241, 78889, 97281, 118721, 143529, 172041, 204609, 241601, 283401, |
|||
330409, 383041, 441729, 506921, 579081, 658689, 746241, 842249, 947241, |
|||
1061761, 1186369, 1321641, 1468169, 1626561, 1797441, 1981449, 2179241, |
|||
2391489, 2618881, 2862121, 3121929, 3399041, 3694209, 4008201, 4341801, |
|||
4695809, 5071041, 5468329, 5888521, 6332481, 6801089, 7295241, 7815849, |
|||
8363841, 8940161, 9545769, 10181641, 10848769, 11548161, 12280841, 13047849, |
|||
13850241, 14689089, 15565481, 16480521, 17435329, 18431041, 19468809, |
|||
20549801, 21675201, 22846209, 24064041, 25329929, 26645121, 28010881, |
|||
29428489, 30899241, 32424449, 34005441, 35643561, 37340169, 39096641, |
|||
40914369, 42794761, 44739241, 46749249, 48826241, 50971689, 53187081, |
|||
55473921, 57833729, 60268041, 62778409, 65366401, 68033601, 70781609, |
|||
73612041, 76526529, 79526721, 82614281, 85790889, 89058241, 92418049, |
|||
95872041, 99421961, 103069569, 106816641, 110664969, 114616361, 118672641, |
|||
122835649, 127107241, 131489289, 135983681, 140592321, 145317129, 150160041, |
|||
155123009, 160208001, 165417001, 170752009, 176215041, 181808129, 187533321, |
|||
193392681, 199388289, 205522241, 211796649, 218213641, 224775361, 231483969, |
|||
238341641, 245350569, 252512961, 259831041, 267307049, 274943241, 282741889, |
|||
290705281, 298835721, 307135529, 315607041, 324252609, 333074601, 342075401, |
|||
351257409, 360623041, 370174729, 379914921, 389846081, 399970689, 410291241, |
|||
420810249, 431530241, 442453761, 453583369, 464921641, 476471169, 488234561, |
|||
500214441, 512413449, 524834241, 537479489, 550351881, 563454121, 576788929, |
|||
590359041, 604167209, 618216201, 632508801, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...208:*/ |
|||
647047809, 661836041, 676876329, 692171521, 707724481, 723538089, 739615241, |
|||
755958849, 772571841, 789457161, 806617769, 824056641, 841776769, 859781161, |
|||
878072841, 896654849, 915530241, 934702089, 954173481, 973947521, 994027329, |
|||
1014416041, 1035116809, 1056132801, 1077467201, 1099123209, 1121104041, |
|||
1143412929, 1166053121, 1189027881, 1212340489, 1235994241, |
|||
#endif |
|||
/*N=6, K=6...96:*/ |
|||
1683, 3653, 7183, 13073, 22363, 36365, 56695, 85305, 124515, 177045, 246047, |
|||
335137, 448427, 590557, 766727, 982729, 1244979, 1560549, 1937199, 2383409, |
|||
2908411, 3522221, 4235671, 5060441, 6009091, 7095093, 8332863, 9737793, |
|||
11326283, 13115773, 15124775, 17372905, 19880915, 22670725, 25765455, |
|||
29189457, 32968347, 37129037, 41699767, 46710137, 52191139, 58175189, |
|||
64696159, 71789409, 79491819, 87841821, 96879431, 106646281, 117185651, |
|||
128542501, 140763503, 153897073, 167993403, 183104493, 199284183, 216588185, |
|||
235074115, 254801525, 275831935, 298228865, 322057867, 347386557, 374284647, |
|||
402823977, 433078547, 465124549, 499040399, 534906769, 572806619, 612825229, |
|||
655050231, 699571641, 746481891, 795875861, 847850911, 902506913, 959946283, |
|||
1020274013, 1083597703, 1150027593, 1219676595, 1292660325, 1369097135, |
|||
1449108145, 1532817275, 1620351277, 1711839767, 1807415257, 1907213187, |
|||
2011371957, 2120032959, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...109:*/ |
|||
2233340609U, 2351442379U, 2474488829U, 2602633639U, 2736033641U, 2874848851U, |
|||
3019242501U, 3169381071U, 3325434321U, 3487575323U, 3655980493U, 3830829623U, |
|||
4012305913U, |
|||
#endif |
|||
/*N=7, K=7...54*/ |
|||
8989, 19825, 40081, 75517, 134245, 227305, 369305, 579125, 880685, 1303777, |
|||
1884961, 2668525, 3707509, 5064793, 6814249, 9041957, 11847485, 15345233, |
|||
19665841, 24957661, 31388293, 39146185, 48442297, 59511829, 72616013, |
|||
88043969, 106114625, 127178701, 151620757, 179861305, 212358985, 249612805, |
|||
292164445, 340600625, 395555537, 457713341, 527810725, 606639529, 695049433, |
|||
793950709, 904317037, 1027188385, 1163673953, 1314955181, 1482288821, |
|||
1667010073, 1870535785, 2094367717, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...60:*/ |
|||
2340095869U, 2609401873U, 2904062449U, 3225952925U, 3577050821U, 3959439497U, |
|||
#endif |
|||
/*N=8, K=8...37*/ |
|||
48639, 108545, 224143, 433905, 795455, 1392065, 2340495, 3800305, 5984767, |
|||
9173505, 13726991, 20103025, 28875327, 40754369, 56610575, 77500017, |
|||
104692735, 139703809, 184327311, 240673265, 311207743, 398796225, 506750351, |
|||
638878193, 799538175, 993696769, 1226990095, 1505789553, 1837271615, |
|||
2229491905U, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...40:*/ |
|||
2691463695U, 3233240945U, 3866006015U, |
|||
#endif |
|||
/*N=9, K=9...28:*/ |
|||
265729, 598417, 1256465, 2485825, 4673345, 8405905, 14546705, 24331777, |
|||
39490049, 62390545, 96220561, 145198913, 214828609, 312193553, 446304145, |
|||
628496897, 872893441, 1196924561, 1621925137, 2173806145U, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...29:*/ |
|||
2883810113U, |
|||
#endif |
|||
/*N=10, K=10...24:*/ |
|||
1462563, 3317445, 7059735, 14218905, 27298155, 50250765, 89129247, 152951073, |
|||
254831667, 413442773, 654862247, 1014889769, 1541911931, 2300409629U, |
|||
3375210671U, |
|||
/*N=11, K=11...19:*/ |
|||
8097453, 18474633, 39753273, 81270333, 158819253, 298199265, 540279585, |
|||
948062325, 1616336765, |
|||
#if defined(CUSTOM_MODES) |
|||
/*...20:*/ |
|||
2684641785U, |
|||
#endif |
|||
/*N=12, K=12...18:*/ |
|||
45046719, 103274625, 224298231, 464387817, 921406335, 1759885185, |
|||
3248227095U, |
|||
/*N=13, K=13...16:*/ |
|||
251595969, 579168825, 1267854873, 2653649025U, |
|||
/*N=14, K=14:*/ |
|||
1409933619 |
|||
}; |
|||
|
|||
#if defined(CUSTOM_MODES) |
|||
static const opus_uint32 *const CELT_PVQ_U_ROW[15]={ |
|||
CELT_PVQ_U_DATA+ 0,CELT_PVQ_U_DATA+ 208,CELT_PVQ_U_DATA+ 415, |
|||
CELT_PVQ_U_DATA+ 621,CELT_PVQ_U_DATA+ 826,CELT_PVQ_U_DATA+1030, |
|||
CELT_PVQ_U_DATA+1233,CELT_PVQ_U_DATA+1336,CELT_PVQ_U_DATA+1389, |
|||
CELT_PVQ_U_DATA+1421,CELT_PVQ_U_DATA+1441,CELT_PVQ_U_DATA+1455, |
|||
CELT_PVQ_U_DATA+1464,CELT_PVQ_U_DATA+1470,CELT_PVQ_U_DATA+1473 |
|||
}; |
|||
#else |
|||
static const opus_uint32 *const CELT_PVQ_U_ROW[15]={ |
|||
CELT_PVQ_U_DATA+ 0,CELT_PVQ_U_DATA+ 176,CELT_PVQ_U_DATA+ 351, |
|||
CELT_PVQ_U_DATA+ 525,CELT_PVQ_U_DATA+ 698,CELT_PVQ_U_DATA+ 870, |
|||
CELT_PVQ_U_DATA+1041,CELT_PVQ_U_DATA+1131,CELT_PVQ_U_DATA+1178, |
|||
CELT_PVQ_U_DATA+1207,CELT_PVQ_U_DATA+1226,CELT_PVQ_U_DATA+1240, |
|||
CELT_PVQ_U_DATA+1248,CELT_PVQ_U_DATA+1254,CELT_PVQ_U_DATA+1257 |
|||
}; |
|||
#endif |
|||
|
|||
#if defined(CUSTOM_MODES) |
|||
void get_required_bits(opus_int16 *_bits,int _n,int _maxk,int _frac){ |
|||
int k; |
|||
/*_maxk==0 => there's nothing to do.*/ |
|||
celt_assert(_maxk>0); |
|||
_bits[0]=0; |
|||
for(k=1;k<=_maxk;k++)_bits[k]=log2_frac(CELT_PVQ_V(_n,k),_frac); |
|||
} |
|||
#endif |
|||
|
|||
static opus_uint32 icwrs(int _n,const int *_y){ |
|||
opus_uint32 i; |
|||
int j; |
|||
int k; |
|||
celt_assert(_n>=2); |
|||
j=_n-1; |
|||
i=_y[j]<0; |
|||
k=abs(_y[j]); |
|||
do{ |
|||
j--; |
|||
i+=CELT_PVQ_U(_n-j,k); |
|||
k+=abs(_y[j]); |
|||
if(_y[j]<0)i+=CELT_PVQ_U(_n-j,k+1); |
|||
} |
|||
while(j>0); |
|||
return i; |
|||
} |
|||
|
|||
void encode_pulses(const int *_y,int _n,int _k,ec_enc *_enc){ |
|||
celt_assert(_k>0); |
|||
ec_enc_uint(_enc,icwrs(_n,_y),CELT_PVQ_V(_n,_k)); |
|||
} |
|||
|
|||
static opus_val32 cwrsi(int _n,int _k,opus_uint32 _i,int *_y){ |
|||
opus_uint32 p; |
|||
int s; |
|||
int k0; |
|||
opus_int16 val; |
|||
opus_val32 yy=0; |
|||
celt_assert(_k>0); |
|||
celt_assert(_n>1); |
|||
while(_n>2){ |
|||
opus_uint32 q; |
|||
/*Lots of pulses case:*/ |
|||
if(_k>=_n){ |
|||
const opus_uint32 *row; |
|||
row=CELT_PVQ_U_ROW[_n]; |
|||
/*Are the pulses in this dimension negative?*/ |
|||
p=row[_k+1]; |
|||
s=-(_i>=p); |
|||
_i-=p&s; |
|||
/*Count how many pulses were placed in this dimension.*/ |
|||
k0=_k; |
|||
q=row[_n]; |
|||
if(q>_i){ |
|||
celt_sig_assert(p>q); |
|||
_k=_n; |
|||
do p=CELT_PVQ_U_ROW[--_k][_n]; |
|||
while(p>_i); |
|||
} |
|||
else for(p=row[_k];p>_i;p=row[_k])_k--; |
|||
_i-=p; |
|||
val=(k0-_k+s)^s; |
|||
*_y++=val; |
|||
yy=MAC16_16(yy,val,val); |
|||
} |
|||
/*Lots of dimensions case:*/ |
|||
else{ |
|||
/*Are there any pulses in this dimension at all?*/ |
|||
p=CELT_PVQ_U_ROW[_k][_n]; |
|||
q=CELT_PVQ_U_ROW[_k+1][_n]; |
|||
if(p<=_i&&_i<q){ |
|||
_i-=p; |
|||
*_y++=0; |
|||
} |
|||
else{ |
|||
/*Are the pulses in this dimension negative?*/ |
|||
s=-(_i>=q); |
|||
_i-=q&s; |
|||
/*Count how many pulses were placed in this dimension.*/ |
|||
k0=_k; |
|||
do p=CELT_PVQ_U_ROW[--_k][_n]; |
|||
while(p>_i); |
|||
_i-=p; |
|||
val=(k0-_k+s)^s; |
|||
*_y++=val; |
|||
yy=MAC16_16(yy,val,val); |
|||
} |
|||
} |
|||
_n--; |
|||
} |
|||
/*_n==2*/ |
|||
p=2*_k+1; |
|||
s=-(_i>=p); |
|||
_i-=p&s; |
|||
k0=_k; |
|||
_k=(_i+1)>>1; |
|||
if(_k)_i-=2*_k-1; |
|||
val=(k0-_k+s)^s; |
|||
*_y++=val; |
|||
yy=MAC16_16(yy,val,val); |
|||
/*_n==1*/ |
|||
s=-(int)_i; |
|||
val=(_k+s)^s; |
|||
*_y=val; |
|||
yy=MAC16_16(yy,val,val); |
|||
return yy; |
|||
} |
|||
|
|||
opus_val32 decode_pulses(int *_y,int _n,int _k,ec_dec *_dec){ |
|||
return cwrsi(_n,_k,ec_dec_uint(_dec,CELT_PVQ_V(_n,_k)),_y); |
|||
} |
|||
|
|||
#else /* SMALL_FOOTPRINT */ |
|||
|
|||
/*Computes the next row/column of any recurrence that obeys the relation
|
|||
u[i][j]=u[i-1][j]+u[i][j-1]+u[i-1][j-1]. |
|||
_ui0 is the base case for the new row/column.*/ |
|||
static OPUS_INLINE void unext(opus_uint32 *_ui,unsigned _len,opus_uint32 _ui0){ |
|||
opus_uint32 ui1; |
|||
unsigned j; |
|||
/*This do-while will overrun the array if we don't have storage for at least
|
|||
2 values.*/ |
|||
j=1; do { |
|||
ui1=UADD32(UADD32(_ui[j],_ui[j-1]),_ui0); |
|||
_ui[j-1]=_ui0; |
|||
_ui0=ui1; |
|||
} while (++j<_len); |
|||
_ui[j-1]=_ui0; |
|||
} |
|||
|
|||
/*Computes the previous row/column of any recurrence that obeys the relation
|
|||
u[i-1][j]=u[i][j]-u[i][j-1]-u[i-1][j-1]. |
|||
_ui0 is the base case for the new row/column.*/ |
|||
static OPUS_INLINE void uprev(opus_uint32 *_ui,unsigned _n,opus_uint32 _ui0){ |
|||
opus_uint32 ui1; |
|||
unsigned j; |
|||
/*This do-while will overrun the array if we don't have storage for at least
|
|||
2 values.*/ |
|||
j=1; do { |
|||
ui1=USUB32(USUB32(_ui[j],_ui[j-1]),_ui0); |
|||
_ui[j-1]=_ui0; |
|||
_ui0=ui1; |
|||
} while (++j<_n); |
|||
_ui[j-1]=_ui0; |
|||
} |
|||
|
|||
/*Compute V(_n,_k), as well as U(_n,0..._k+1).
|
|||
_u: On exit, _u[i] contains U(_n,i) for i in [0..._k+1].*/ |
|||
static opus_uint32 ncwrs_urow(unsigned _n,unsigned _k,opus_uint32 *_u){ |
|||
opus_uint32 um2; |
|||
unsigned len; |
|||
unsigned k; |
|||
len=_k+2; |
|||
/*We require storage at least 3 values (e.g., _k>0).*/ |
|||
celt_assert(len>=3); |
|||
_u[0]=0; |
|||
_u[1]=um2=1; |
|||
/*If _n==0, _u[0] should be 1 and the rest should be 0.*/ |
|||
/*If _n==1, _u[i] should be 1 for i>1.*/ |
|||
celt_assert(_n>=2); |
|||
/*If _k==0, the following do-while loop will overflow the buffer.*/ |
|||
celt_assert(_k>0); |
|||
k=2; |
|||
do _u[k]=(k<<1)-1; |
|||
while(++k<len); |
|||
for(k=2;k<_n;k++)unext(_u+1,_k+1,1); |
|||
return _u[_k]+_u[_k+1]; |
|||
} |
|||
|
|||
/*Returns the _i'th combination of _k elements chosen from a set of size _n
|
|||
with associated sign bits. |
|||
_y: Returns the vector of pulses. |
|||
_u: Must contain entries [0..._k+1] of row _n of U() on input. |
|||
Its contents will be destructively modified.*/ |
|||
static opus_val32 cwrsi(int _n,int _k,opus_uint32 _i,int *_y,opus_uint32 *_u){ |
|||
int j; |
|||
opus_int16 val; |
|||
opus_val32 yy=0; |
|||
celt_assert(_n>0); |
|||
j=0; |
|||
do{ |
|||
opus_uint32 p; |
|||
int s; |
|||
int yj; |
|||
p=_u[_k+1]; |
|||
s=-(_i>=p); |
|||
_i-=p&s; |
|||
yj=_k; |
|||
p=_u[_k]; |
|||
while(p>_i)p=_u[--_k]; |
|||
_i-=p; |
|||
yj-=_k; |
|||
val=(yj+s)^s; |
|||
_y[j]=val; |
|||
yy=MAC16_16(yy,val,val); |
|||
uprev(_u,_k+2,0); |
|||
} |
|||
while(++j<_n); |
|||
return yy; |
|||
} |
|||
|
|||
/*Returns the index of the given combination of K elements chosen from a set
|
|||
of size 1 with associated sign bits. |
|||
_y: The vector of pulses, whose sum of absolute values is K. |
|||
_k: Returns K.*/ |
|||
static OPUS_INLINE opus_uint32 icwrs1(const int *_y,int *_k){ |
|||
*_k=abs(_y[0]); |
|||
return _y[0]<0; |
|||
} |
|||
|
|||
/*Returns the index of the given combination of K elements chosen from a set
|
|||
of size _n with associated sign bits. |
|||
_y: The vector of pulses, whose sum of absolute values must be _k. |
|||
_nc: Returns V(_n,_k).*/ |
|||
static OPUS_INLINE opus_uint32 icwrs(int _n,int _k,opus_uint32 *_nc,const int *_y, |
|||
opus_uint32 *_u){ |
|||
opus_uint32 i; |
|||
int j; |
|||
int k; |
|||
/*We can't unroll the first two iterations of the loop unless _n>=2.*/ |
|||
celt_assert(_n>=2); |
|||
_u[0]=0; |
|||
for(k=1;k<=_k+1;k++)_u[k]=(k<<1)-1; |
|||
i=icwrs1(_y+_n-1,&k); |
|||
j=_n-2; |
|||
i+=_u[k]; |
|||
k+=abs(_y[j]); |
|||
if(_y[j]<0)i+=_u[k+1]; |
|||
while(j-->0){ |
|||
unext(_u,_k+2,0); |
|||
i+=_u[k]; |
|||
k+=abs(_y[j]); |
|||
if(_y[j]<0)i+=_u[k+1]; |
|||
} |
|||
*_nc=_u[k]+_u[k+1]; |
|||
return i; |
|||
} |
|||
|
|||
#ifdef CUSTOM_MODES |
|||
void get_required_bits(opus_int16 *_bits,int _n,int _maxk,int _frac){ |
|||
int k; |
|||
/*_maxk==0 => there's nothing to do.*/ |
|||
celt_assert(_maxk>0); |
|||
_bits[0]=0; |
|||
if (_n==1) |
|||
{ |
|||
for (k=1;k<=_maxk;k++) |
|||
_bits[k] = 1<<_frac; |
|||
} |
|||
else { |
|||
VARDECL(opus_uint32,u); |
|||
SAVE_STACK; |
|||
ALLOC(u,_maxk+2U,opus_uint32); |
|||
ncwrs_urow(_n,_maxk,u); |
|||
for(k=1;k<=_maxk;k++) |
|||
_bits[k]=log2_frac(u[k]+u[k+1],_frac); |
|||
RESTORE_STACK; |
|||
} |
|||
} |
|||
#endif /* CUSTOM_MODES */ |
|||
|
|||
void encode_pulses(const int *_y,int _n,int _k,ec_enc *_enc){ |
|||
opus_uint32 i; |
|||
VARDECL(opus_uint32,u); |
|||
opus_uint32 nc; |
|||
SAVE_STACK; |
|||
celt_assert(_k>0); |
|||
ALLOC(u,_k+2U,opus_uint32); |
|||
i=icwrs(_n,_k,&nc,_y,u); |
|||
ec_enc_uint(_enc,i,nc); |
|||
RESTORE_STACK; |
|||
} |
|||
|
|||
opus_val32 decode_pulses(int *_y,int _n,int _k,ec_dec *_dec){ |
|||
VARDECL(opus_uint32,u); |
|||
int ret; |
|||
SAVE_STACK; |
|||
celt_assert(_k>0); |
|||
ALLOC(u,_k+2U,opus_uint32); |
|||
ret = cwrsi(_n,_k,ec_dec_uint(_dec,ncwrs_urow(_n,_k,u)),_y,u); |
|||
RESTORE_STACK; |
|||
return ret; |
|||
} |
|||
|
|||
#endif /* SMALL_FOOTPRINT */ |
|||
@ -0,0 +1,48 @@ |
|||
/* Copyright (c) 2007-2008 CSIRO
|
|||
Copyright (c) 2007-2009 Xiph.Org Foundation |
|||
Copyright (c) 2007-2009 Timothy B. Terriberry |
|||
Written by Timothy B. Terriberry and Jean-Marc Valin */ |
|||
/*
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
- Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
|||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
#ifndef CWRS_H |
|||
#define CWRS_H |
|||
|
|||
#include "arch.h" |
|||
#include "stack_alloc.h" |
|||
#include "entenc.h" |
|||
#include "entdec.h" |
|||
|
|||
#ifdef CUSTOM_MODES |
|||
int log2_frac(opus_uint32 val, int frac); |
|||
#endif |
|||
|
|||
void get_required_bits(opus_int16 *bits, int N, int K, int frac); |
|||
|
|||
void encode_pulses(const int *_y, int N, int K, ec_enc *enc); |
|||
|
|||
opus_val32 decode_pulses(int *_y, int N, int K, ec_dec *dec); |
|||
|
|||
#endif /* CWRS_H */ |
|||
@ -0,0 +1,91 @@ |
|||
/* Copyright (c) 2003-2008 Timothy B. Terriberry
|
|||
Copyright (c) 2008 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. |
|||
*/ |
|||
|
|||
/*Some common macros for potential platform-specific optimization.*/ |
|||
#include "opus_types.h" |
|||
#include <math.h> |
|||
#include <limits.h> |
|||
#include "arch.h" |
|||
#if !defined(_ecintrin_H) |
|||
# define _ecintrin_H (1) |
|||
|
|||
/*Some specific platforms may have optimized intrinsic or OPUS_INLINE assembly
|
|||
versions of these functions which can substantially improve performance. |
|||
We define macros for them to allow easy incorporation of these non-ANSI |
|||
features.*/ |
|||
|
|||
/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if
|
|||
given an appropriate architecture, but the branchless bit-twiddling versions |
|||
are just as fast, and do not require any special target architecture. |
|||
Earlier gcc versions (3.x) compiled both code to the same assembly |
|||
instructions, because of the way they represented ((_b)>(_a)) internally.*/ |
|||
# define EC_MINI(_a,_b) ((_a)+(((_b)-(_a))&-((_b)<(_a)))) |
|||
|
|||
/*Count leading zeros.
|
|||
This macro should only be used for implementing ec_ilog(), if it is defined. |
|||
All other code should use EC_ILOG() instead.*/ |
|||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) |
|||
#if defined(_MSC_VER) && (_MSC_VER >= 1910) |
|||
# include <intrin0.h> /* Improve compiler throughput. */ |
|||
#else |
|||
# include <intrin.h> |
|||
#endif |
|||
/*In _DEBUG mode this is not an intrinsic by default.*/ |
|||
# pragma intrinsic(_BitScanReverse) |
|||
|
|||
static __inline int ec_bsr(unsigned long _x){ |
|||
unsigned long ret; |
|||
_BitScanReverse(&ret,_x); |
|||
return (int)ret; |
|||
} |
|||
# define EC_CLZ0 (1) |
|||
# define EC_CLZ(_x) (-ec_bsr(_x)) |
|||
#elif defined(ENABLE_TI_DSPLIB) |
|||
# include "dsplib.h" |
|||
# define EC_CLZ0 (31) |
|||
# define EC_CLZ(_x) (_lnorm(_x)) |
|||
#elif __GNUC_PREREQ(3,4) |
|||
# if INT_MAX>=2147483647 |
|||
# define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT) |
|||
# define EC_CLZ(_x) (__builtin_clz(_x)) |
|||
# elif LONG_MAX>=2147483647L |
|||
# define EC_CLZ0 ((int)sizeof(unsigned long)*CHAR_BIT) |
|||
# define EC_CLZ(_x) (__builtin_clzl(_x)) |
|||
# endif |
|||
#endif |
|||
|
|||
#if defined(EC_CLZ) |
|||
/*Note that __builtin_clz is not defined when _x==0, according to the gcc
|
|||
documentation (and that of the BSR instruction that implements it on x86). |
|||
The majority of the time we can never pass it zero. |
|||
When we need to, it can be special cased.*/ |
|||
# define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x)) |
|||
#else |
|||
int ec_ilog(opus_uint32 _v); |
|||
# define EC_ILOG(_x) (ec_ilog(_x)) |
|||
#endif |
|||
#endif |
|||
@ -0,0 +1,153 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
*/ |
|||
/*
|
|||
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 "entcode.h" |
|||
#include "arch.h" |
|||
|
|||
#if !defined(EC_CLZ) |
|||
/*This is a fallback for systems where we don't know how to access
|
|||
a BSR or CLZ instruction (see ecintrin.h). |
|||
If you are optimizing Opus on a new platform and it has a native CLZ or |
|||
BZR (e.g. cell, MIPS, x86, etc) then making it available to Opus will be |
|||
an easy performance win.*/ |
|||
int ec_ilog(opus_uint32 _v){ |
|||
/*On a Pentium M, this branchless version tested as the fastest on
|
|||
1,000,000,000 random 32-bit integers, edging out a similar version with |
|||
branches, and a 256-entry LUT version.*/ |
|||
int ret; |
|||
int m; |
|||
ret=!!_v; |
|||
m=!!(_v&0xFFFF0000)<<4; |
|||
_v>>=m; |
|||
ret|=m; |
|||
m=!!(_v&0xFF00)<<3; |
|||
_v>>=m; |
|||
ret|=m; |
|||
m=!!(_v&0xF0)<<2; |
|||
_v>>=m; |
|||
ret|=m; |
|||
m=!!(_v&0xC)<<1; |
|||
_v>>=m; |
|||
ret|=m; |
|||
ret+=!!(_v&0x2); |
|||
return ret; |
|||
} |
|||
#endif |
|||
|
|||
#if 1 |
|||
/* This is a faster version of ec_tell_frac() that takes advantage
|
|||
of the low (1/8 bit) resolution to use just a linear function |
|||
followed by a lookup to determine the exact transition thresholds. */ |
|||
opus_uint32 ec_tell_frac(ec_ctx *_this){ |
|||
static const unsigned correction[8] = |
|||
{35733, 38967, 42495, 46340, |
|||
50535, 55109, 60097, 65535}; |
|||
opus_uint32 nbits; |
|||
opus_uint32 r; |
|||
int l; |
|||
unsigned b; |
|||
nbits=_this->nbits_total<<BITRES; |
|||
l=EC_ILOG(_this->rng); |
|||
r=_this->rng>>(l-16); |
|||
b = (r>>12)-8; |
|||
b += r>correction[b]; |
|||
l = (l<<3)+b; |
|||
return nbits-l; |
|||
} |
|||
#else |
|||
opus_uint32 ec_tell_frac(ec_ctx *_this){ |
|||
opus_uint32 nbits; |
|||
opus_uint32 r; |
|||
int l; |
|||
int i; |
|||
/*To handle the non-integral number of bits still left in the encoder/decoder
|
|||
state, we compute the worst-case number of bits of val that must be |
|||
encoded to ensure that the value is inside the range for any possible |
|||
subsequent bits. |
|||
The computation here is independent of val itself (the decoder does not |
|||
even track that value), even though the real number of bits used after |
|||
ec_enc_done() may be 1 smaller if rng is a power of two and the |
|||
corresponding trailing bits of val are all zeros. |
|||
If we did try to track that special case, then coding a value with a |
|||
probability of 1/(1<<n) might sometimes appear to use more than n bits. |
|||
This may help explain the surprising result that a newly initialized |
|||
encoder or decoder claims to have used 1 bit.*/ |
|||
nbits=_this->nbits_total<<BITRES; |
|||
l=EC_ILOG(_this->rng); |
|||
r=_this->rng>>(l-16); |
|||
for(i=BITRES;i-->0;){ |
|||
int b; |
|||
r=r*r>>15; |
|||
b=(int)(r>>16); |
|||
l=l<<1|b; |
|||
r>>=b; |
|||
} |
|||
return nbits-l; |
|||
} |
|||
#endif |
|||
|
|||
#ifdef USE_SMALL_DIV_TABLE |
|||
/* Result of 2^32/(2*i+1), except for i=0. */ |
|||
const opus_uint32 SMALL_DIV_TABLE[129] = { |
|||
0xFFFFFFFF, 0x55555555, 0x33333333, 0x24924924, |
|||
0x1C71C71C, 0x1745D174, 0x13B13B13, 0x11111111, |
|||
0x0F0F0F0F, 0x0D79435E, 0x0C30C30C, 0x0B21642C, |
|||
0x0A3D70A3, 0x097B425E, 0x08D3DCB0, 0x08421084, |
|||
0x07C1F07C, 0x07507507, 0x06EB3E45, 0x06906906, |
|||
0x063E7063, 0x05F417D0, 0x05B05B05, 0x0572620A, |
|||
0x05397829, 0x05050505, 0x04D4873E, 0x04A7904A, |
|||
0x047DC11F, 0x0456C797, 0x04325C53, 0x04104104, |
|||
0x03F03F03, 0x03D22635, 0x03B5CC0E, 0x039B0AD1, |
|||
0x0381C0E0, 0x0369D036, 0x03531DEC, 0x033D91D2, |
|||
0x0329161F, 0x03159721, 0x03030303, 0x02F14990, |
|||
0x02E05C0B, 0x02D02D02, 0x02C0B02C, 0x02B1DA46, |
|||
0x02A3A0FD, 0x0295FAD4, 0x0288DF0C, 0x027C4597, |
|||
0x02702702, 0x02647C69, 0x02593F69, 0x024E6A17, |
|||
0x0243F6F0, 0x0239E0D5, 0x02302302, 0x0226B902, |
|||
0x021D9EAD, 0x0214D021, 0x020C49BA, 0x02040810, |
|||
0x01FC07F0, 0x01F44659, 0x01ECC07B, 0x01E573AC, |
|||
0x01DE5D6E, 0x01D77B65, 0x01D0CB58, 0x01CA4B30, |
|||
0x01C3F8F0, 0x01BDD2B8, 0x01B7D6C3, 0x01B20364, |
|||
0x01AC5701, 0x01A6D01A, 0x01A16D3F, 0x019C2D14, |
|||
0x01970E4F, 0x01920FB4, 0x018D3018, 0x01886E5F, |
|||
0x0183C977, 0x017F405F, 0x017AD220, 0x01767DCE, |
|||
0x01724287, 0x016E1F76, 0x016A13CD, 0x01661EC6, |
|||
0x01623FA7, 0x015E75BB, 0x015AC056, 0x01571ED3, |
|||
0x01539094, 0x01501501, 0x014CAB88, 0x0149539E, |
|||
0x01460CBC, 0x0142D662, 0x013FB013, 0x013C995A, |
|||
0x013991C2, 0x013698DF, 0x0133AE45, 0x0130D190, |
|||
0x012E025C, 0x012B404A, 0x01288B01, 0x0125E227, |
|||
0x01234567, 0x0120B470, 0x011E2EF3, 0x011BB4A4, |
|||
0x01194538, 0x0116E068, 0x011485F0, 0x0112358E, |
|||
0x010FEF01, 0x010DB20A, 0x010B7E6E, 0x010953F3, |
|||
0x01073260, 0x0105197F, 0x0103091B, 0x01010101 |
|||
}; |
|||
#endif |
|||
@ -0,0 +1,152 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
Copyright (c) 2008-2009 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. |
|||
*/ |
|||
|
|||
#include "opus_types.h" |
|||
#include "opus_defines.h" |
|||
|
|||
#if !defined(_entcode_H) |
|||
# define _entcode_H (1) |
|||
# include <limits.h> |
|||
# include <stddef.h> |
|||
# include "ecintrin.h" |
|||
|
|||
extern const opus_uint32 SMALL_DIV_TABLE[129]; |
|||
|
|||
#ifdef OPUS_ARM_ASM |
|||
#define USE_SMALL_DIV_TABLE |
|||
#endif |
|||
|
|||
/*OPT: ec_window must be at least 32 bits, but if you have fast arithmetic on a
|
|||
larger type, you can speed up the decoder by using it here.*/ |
|||
typedef opus_uint32 ec_window; |
|||
typedef struct ec_ctx ec_ctx; |
|||
typedef struct ec_ctx ec_enc; |
|||
typedef struct ec_ctx ec_dec; |
|||
|
|||
# define EC_WINDOW_SIZE ((int)sizeof(ec_window)*CHAR_BIT) |
|||
|
|||
/*The number of bits to use for the range-coded part of unsigned integers.*/ |
|||
# define EC_UINT_BITS (8) |
|||
|
|||
/*The resolution of fractional-precision bit usage measurements, i.e.,
|
|||
3 => 1/8th bits.*/ |
|||
# define BITRES 3 |
|||
|
|||
/*The entropy encoder/decoder context.
|
|||
We use the same structure for both, so that common functions like ec_tell() |
|||
can be used on either one.*/ |
|||
struct ec_ctx{ |
|||
/*Buffered input/output.*/ |
|||
unsigned char *buf; |
|||
/*The size of the buffer.*/ |
|||
opus_uint32 storage; |
|||
/*The offset at which the last byte containing raw bits was read/written.*/ |
|||
opus_uint32 end_offs; |
|||
/*Bits that will be read from/written at the end.*/ |
|||
ec_window end_window; |
|||
/*Number of valid bits in end_window.*/ |
|||
int nend_bits; |
|||
/*The total number of whole bits read/written.
|
|||
This does not include partial bits currently in the range coder.*/ |
|||
int nbits_total; |
|||
/*The offset at which the next range coder byte will be read/written.*/ |
|||
opus_uint32 offs; |
|||
/*The number of values in the current range.*/ |
|||
opus_uint32 rng; |
|||
/*In the decoder: the difference between the top of the current range and
|
|||
the input value, minus one. |
|||
In the encoder: the low end of the current range.*/ |
|||
opus_uint32 val; |
|||
/*In the decoder: the saved normalization factor from ec_decode().
|
|||
In the encoder: the number of oustanding carry propagating symbols.*/ |
|||
opus_uint32 ext; |
|||
/*A buffered input/output symbol, awaiting carry propagation.*/ |
|||
int rem; |
|||
/*Nonzero if an error occurred.*/ |
|||
int error; |
|||
}; |
|||
|
|||
static OPUS_INLINE opus_uint32 ec_range_bytes(ec_ctx *_this){ |
|||
return _this->offs; |
|||
} |
|||
|
|||
static OPUS_INLINE unsigned char *ec_get_buffer(ec_ctx *_this){ |
|||
return _this->buf; |
|||
} |
|||
|
|||
static OPUS_INLINE int ec_get_error(ec_ctx *_this){ |
|||
return _this->error; |
|||
} |
|||
|
|||
/*Returns the number of bits "used" by the encoded or decoded symbols so far.
|
|||
This same number can be computed in either the encoder or the decoder, and is |
|||
suitable for making coding decisions. |
|||
Return: The number of bits. |
|||
This will always be slightly larger than the exact value (e.g., all |
|||
rounding error is in the positive direction).*/ |
|||
static OPUS_INLINE int ec_tell(ec_ctx *_this){ |
|||
return _this->nbits_total-EC_ILOG(_this->rng); |
|||
} |
|||
|
|||
/*Returns the number of bits "used" by the encoded or decoded symbols so far.
|
|||
This same number can be computed in either the encoder or the decoder, and is |
|||
suitable for making coding decisions. |
|||
Return: The number of bits scaled by 2**BITRES. |
|||
This will always be slightly larger than the exact value (e.g., all |
|||
rounding error is in the positive direction).*/ |
|||
opus_uint32 ec_tell_frac(ec_ctx *_this); |
|||
|
|||
/* Tested exhaustively for all n and for 1<=d<=256 */ |
|||
static OPUS_INLINE opus_uint32 celt_udiv(opus_uint32 n, opus_uint32 d) { |
|||
celt_sig_assert(d>0); |
|||
#ifdef USE_SMALL_DIV_TABLE |
|||
if (d>256) |
|||
return n/d; |
|||
else { |
|||
opus_uint32 t, q; |
|||
t = EC_ILOG(d&-d); |
|||
q = (opus_uint64)SMALL_DIV_TABLE[d>>t]*(n>>(t-1))>>32; |
|||
return q+(n-q*d >= d); |
|||
} |
|||
#else |
|||
return n/d; |
|||
#endif |
|||
} |
|||
|
|||
static OPUS_INLINE opus_int32 celt_sudiv(opus_int32 n, opus_int32 d) { |
|||
celt_sig_assert(d>0); |
|||
#ifdef USE_SMALL_DIV_TABLE |
|||
if (n<0) |
|||
return -(opus_int32)celt_udiv(-n, d); |
|||
else |
|||
return celt_udiv(n, d); |
|||
#else |
|||
return n/d; |
|||
#endif |
|||
} |
|||
|
|||
#endif |
|||
@ -0,0 +1,266 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
Copyright (c) 2008-2009 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. |
|||
*/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#include <stddef.h> |
|||
#include "os_support.h" |
|||
#include "arch.h" |
|||
#include "entdec.h" |
|||
#include "mfrngcod.h" |
|||
|
|||
/*A range decoder.
|
|||
This is an entropy decoder based upon \cite{Mar79}, which is itself a |
|||
rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}. |
|||
It is very similar to arithmetic encoding, except that encoding is done with |
|||
digits in any base, instead of with bits, and so it is faster when using |
|||
larger bases (i.e.: a byte). |
|||
The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$ |
|||
is the base, longer than the theoretical optimum, but to my knowledge there |
|||
is no published justification for this claim. |
|||
This only seems true when using near-infinite precision arithmetic so that |
|||
the process is carried out with no rounding errors. |
|||
|
|||
An excellent description of implementation details is available at |
|||
http://www.arturocampos.com/ac_range.html
|
|||
A recent work \cite{MNW98} which proposes several changes to arithmetic |
|||
encoding for efficiency actually re-discovers many of the principles |
|||
behind range encoding, and presents a good theoretical analysis of them. |
|||
|
|||
End of stream is handled by writing out the smallest number of bits that |
|||
ensures that the stream will be correctly decoded regardless of the value of |
|||
any subsequent bits. |
|||
ec_tell() can be used to determine how many bits were needed to decode |
|||
all the symbols thus far; other data can be packed in the remaining bits of |
|||
the input buffer. |
|||
@PHDTHESIS{Pas76, |
|||
author="Richard Clark Pasco", |
|||
title="Source coding algorithms for fast data compression", |
|||
school="Dept. of Electrical Engineering, Stanford University", |
|||
address="Stanford, CA", |
|||
month=May, |
|||
year=1976 |
|||
} |
|||
@INPROCEEDINGS{Mar79, |
|||
author="Martin, G.N.N.", |
|||
title="Range encoding: an algorithm for removing redundancy from a digitised |
|||
message", |
|||
booktitle="Video & Data Recording Conference", |
|||
year=1979, |
|||
address="Southampton", |
|||
month=Jul |
|||
} |
|||
@ARTICLE{MNW98, |
|||
author="Alistair Moffat and Radford Neal and Ian H. Witten", |
|||
title="Arithmetic Coding Revisited", |
|||
journal="{ACM} Transactions on Information Systems", |
|||
year=1998, |
|||
volume=16, |
|||
number=3, |
|||
pages="256--294", |
|||
month=Jul, |
|||
URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf" |
|||
}*/ |
|||
|
|||
static int ec_read_byte(ec_dec *_this){ |
|||
return _this->offs<_this->storage?_this->buf[_this->offs++]:0; |
|||
} |
|||
|
|||
static int ec_read_byte_from_end(ec_dec *_this){ |
|||
return _this->end_offs<_this->storage? |
|||
_this->buf[_this->storage-++(_this->end_offs)]:0; |
|||
} |
|||
|
|||
/*Normalizes the contents of val and rng so that rng lies entirely in the
|
|||
high-order symbol.*/ |
|||
static void ec_dec_normalize(ec_dec *_this){ |
|||
/*If the range is too small, rescale it and input some bits.*/ |
|||
while(_this->rng<=EC_CODE_BOT){ |
|||
int sym; |
|||
_this->nbits_total+=EC_SYM_BITS; |
|||
_this->rng<<=EC_SYM_BITS; |
|||
/*Use up the remaining bits from our last symbol.*/ |
|||
sym=_this->rem; |
|||
/*Read the next value from the input.*/ |
|||
_this->rem=ec_read_byte(_this); |
|||
/*Take the rest of the bits we need from this new symbol.*/ |
|||
sym=(sym<<EC_SYM_BITS|_this->rem)>>(EC_SYM_BITS-EC_CODE_EXTRA); |
|||
/*And subtract them from val, capped to be less than EC_CODE_TOP.*/ |
|||
_this->val=((_this->val<<EC_SYM_BITS)+(EC_SYM_MAX&~sym))&(EC_CODE_TOP-1); |
|||
} |
|||
} |
|||
|
|||
void ec_dec_init(ec_dec *_this,unsigned char *_buf,opus_uint32 _storage){ |
|||
_this->buf=_buf; |
|||
_this->storage=_storage; |
|||
_this->end_offs=0; |
|||
_this->end_window=0; |
|||
_this->nend_bits=0; |
|||
/*This is the offset from which ec_tell() will subtract partial bits.
|
|||
The final value after the ec_dec_normalize() call will be the same as in |
|||
the encoder, but we have to compensate for the bits that are added there.*/ |
|||
_this->nbits_total=EC_CODE_BITS+1 |
|||
-((EC_CODE_BITS-EC_CODE_EXTRA)/EC_SYM_BITS)*EC_SYM_BITS; |
|||
_this->offs=0; |
|||
_this->rng=1U<<EC_CODE_EXTRA; |
|||
_this->rem=ec_read_byte(_this); |
|||
_this->val=_this->rng-1-(_this->rem>>(EC_SYM_BITS-EC_CODE_EXTRA)); |
|||
_this->error=0; |
|||
/*Normalize the interval.*/ |
|||
ec_dec_normalize(_this); |
|||
} |
|||
|
|||
unsigned ec_decode(ec_dec *_this,unsigned _ft){ |
|||
unsigned s; |
|||
_this->ext=celt_udiv(_this->rng,_ft); |
|||
s=(unsigned)(_this->val/_this->ext); |
|||
return _ft-EC_MINI(s+1,_ft); |
|||
} |
|||
|
|||
unsigned ec_decode_bin(ec_dec *_this,unsigned _bits){ |
|||
unsigned s; |
|||
_this->ext=_this->rng>>_bits; |
|||
s=(unsigned)(_this->val/_this->ext); |
|||
return (1U<<_bits)-EC_MINI(s+1U,1U<<_bits); |
|||
} |
|||
|
|||
void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft){ |
|||
opus_uint32 s; |
|||
s=IMUL32(_this->ext,_ft-_fh); |
|||
_this->val-=s; |
|||
_this->rng=_fl>0?IMUL32(_this->ext,_fh-_fl):_this->rng-s; |
|||
ec_dec_normalize(_this); |
|||
} |
|||
|
|||
/*The probability of having a "one" is 1/(1<<_logp).*/ |
|||
int ec_dec_bit_logp(ec_dec *_this,unsigned _logp){ |
|||
opus_uint32 r; |
|||
opus_uint32 d; |
|||
opus_uint32 s; |
|||
int ret; |
|||
r=_this->rng; |
|||
d=_this->val; |
|||
s=r>>_logp; |
|||
ret=d<s; |
|||
if(!ret)_this->val=d-s; |
|||
_this->rng=ret?s:r-s; |
|||
ec_dec_normalize(_this); |
|||
return ret; |
|||
} |
|||
|
|||
int ec_dec_icdf(ec_dec *_this,const unsigned char *_icdf,unsigned _ftb){ |
|||
opus_uint32 r; |
|||
opus_uint32 d; |
|||
opus_uint32 s; |
|||
opus_uint32 t; |
|||
int ret; |
|||
s=_this->rng; |
|||
d=_this->val; |
|||
r=s>>_ftb; |
|||
ret=-1; |
|||
do{ |
|||
t=s; |
|||
s=IMUL32(r,_icdf[++ret]); |
|||
} |
|||
while(d<s); |
|||
_this->val=d-s; |
|||
_this->rng=t-s; |
|||
ec_dec_normalize(_this); |
|||
return ret; |
|||
} |
|||
|
|||
int ec_dec_icdf16(ec_dec *_this,const opus_uint16 *_icdf,unsigned _ftb){ |
|||
opus_uint32 r; |
|||
opus_uint32 d; |
|||
opus_uint32 s; |
|||
opus_uint32 t; |
|||
int ret; |
|||
s=_this->rng; |
|||
d=_this->val; |
|||
r=s>>_ftb; |
|||
ret=-1; |
|||
do{ |
|||
t=s; |
|||
s=IMUL32(r,_icdf[++ret]); |
|||
} |
|||
while(d<s); |
|||
_this->val=d-s; |
|||
_this->rng=t-s; |
|||
ec_dec_normalize(_this); |
|||
return ret; |
|||
} |
|||
|
|||
opus_uint32 ec_dec_uint(ec_dec *_this,opus_uint32 _ft){ |
|||
unsigned ft; |
|||
unsigned s; |
|||
int ftb; |
|||
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ |
|||
celt_assert(_ft>1); |
|||
_ft--; |
|||
ftb=EC_ILOG(_ft); |
|||
if(ftb>EC_UINT_BITS){ |
|||
opus_uint32 t; |
|||
ftb-=EC_UINT_BITS; |
|||
ft=(unsigned)(_ft>>ftb)+1; |
|||
s=ec_decode(_this,ft); |
|||
ec_dec_update(_this,s,s+1,ft); |
|||
t=(opus_uint32)s<<ftb|ec_dec_bits(_this,ftb); |
|||
if(t<=_ft)return t; |
|||
_this->error=1; |
|||
return _ft; |
|||
} |
|||
else{ |
|||
_ft++; |
|||
s=ec_decode(_this,(unsigned)_ft); |
|||
ec_dec_update(_this,s,s+1,(unsigned)_ft); |
|||
return s; |
|||
} |
|||
} |
|||
|
|||
opus_uint32 ec_dec_bits(ec_dec *_this,unsigned _bits){ |
|||
ec_window window; |
|||
int available; |
|||
opus_uint32 ret; |
|||
window=_this->end_window; |
|||
available=_this->nend_bits; |
|||
if((unsigned)available<_bits){ |
|||
do{ |
|||
window|=(ec_window)ec_read_byte_from_end(_this)<<available; |
|||
available+=EC_SYM_BITS; |
|||
} |
|||
while(available<=EC_WINDOW_SIZE-EC_SYM_BITS); |
|||
} |
|||
ret=(opus_uint32)window&(((opus_uint32)1<<_bits)-1U); |
|||
window>>=_bits; |
|||
available-=_bits; |
|||
_this->end_window=window; |
|||
_this->nend_bits=available; |
|||
_this->nbits_total+=_bits; |
|||
return ret; |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
Copyright (c) 2008-2009 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. |
|||
*/ |
|||
|
|||
#if !defined(_entdec_H) |
|||
# define _entdec_H (1) |
|||
# include <limits.h> |
|||
# include "entcode.h" |
|||
|
|||
/*Initializes the decoder.
|
|||
_buf: The input buffer to use. |
|||
Return: 0 on success, or a negative value on error.*/ |
|||
void ec_dec_init(ec_dec *_this,unsigned char *_buf,opus_uint32 _storage); |
|||
|
|||
/*Calculates the cumulative frequency for the next symbol.
|
|||
This can then be fed into the probability model to determine what that |
|||
symbol is, and the additional frequency information required to advance to |
|||
the next symbol. |
|||
This function cannot be called more than once without a corresponding call to |
|||
ec_dec_update(), or decoding will not proceed correctly. |
|||
_ft: The total frequency of the symbols in the alphabet the next symbol was |
|||
encoded with. |
|||
Return: A cumulative frequency representing the encoded symbol. |
|||
If the cumulative frequency of all the symbols before the one that |
|||
was encoded was fl, and the cumulative frequency of all the symbols |
|||
up to and including the one encoded is fh, then the returned value |
|||
will fall in the range [fl,fh).*/ |
|||
unsigned ec_decode(ec_dec *_this,unsigned _ft); |
|||
|
|||
/*Equivalent to ec_decode() with _ft==1<<_bits.*/ |
|||
unsigned ec_decode_bin(ec_dec *_this,unsigned _bits); |
|||
|
|||
/*Advance the decoder past the next symbol using the frequency information the
|
|||
symbol was encoded with. |
|||
Exactly one call to ec_decode() must have been made so that all necessary |
|||
intermediate calculations are performed. |
|||
_fl: The cumulative frequency of all symbols that come before the symbol |
|||
decoded. |
|||
_fh: The cumulative frequency of all symbols up to and including the symbol |
|||
decoded. |
|||
Together with _fl, this defines the range [_fl,_fh) in which the value |
|||
returned above must fall. |
|||
_ft: The total frequency of the symbols in the alphabet the symbol decoded |
|||
was encoded in. |
|||
This must be the same as passed to the preceding call to ec_decode().*/ |
|||
void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft); |
|||
|
|||
/* Decode a bit that has a 1/(1<<_logp) probability of being a one */ |
|||
int ec_dec_bit_logp(ec_dec *_this,unsigned _logp); |
|||
|
|||
/*Decodes a symbol given an "inverse" CDF table.
|
|||
No call to ec_dec_update() is necessary after this call. |
|||
_icdf: The "inverse" CDF, such that symbol s falls in the range |
|||
[s>0?ft-_icdf[s-1]:0,ft-_icdf[s]), where ft=1<<_ftb. |
|||
The values must be monotonically non-increasing, and the last value |
|||
must be 0. |
|||
_ftb: The number of bits of precision in the cumulative distribution. |
|||
Return: The decoded symbol s.*/ |
|||
int ec_dec_icdf(ec_dec *_this,const unsigned char *_icdf,unsigned _ftb); |
|||
|
|||
/*Decodes a symbol given an "inverse" CDF table.
|
|||
No call to ec_dec_update() is necessary after this call. |
|||
_icdf: The "inverse" CDF, such that symbol s falls in the range |
|||
[s>0?ft-_icdf[s-1]:0,ft-_icdf[s]), where ft=1<<_ftb. |
|||
The values must be monotonically non-increasing, and the last value |
|||
must be 0. |
|||
_ftb: The number of bits of precision in the cumulative distribution. |
|||
Return: The decoded symbol s.*/ |
|||
int ec_dec_icdf16(ec_dec *_this,const opus_uint16 *_icdf,unsigned _ftb); |
|||
|
|||
/*Extracts a raw unsigned integer with a non-power-of-2 range from the stream.
|
|||
The bits must have been encoded with ec_enc_uint(). |
|||
No call to ec_dec_update() is necessary after this call. |
|||
_ft: The number of integers that can be decoded (one more than the max). |
|||
This must be at least 2, and no more than 2**32-1. |
|||
Return: The decoded bits.*/ |
|||
opus_uint32 ec_dec_uint(ec_dec *_this,opus_uint32 _ft); |
|||
|
|||
/*Extracts a sequence of raw bits from the stream.
|
|||
The bits must have been encoded with ec_enc_bits(). |
|||
No call to ec_dec_update() is necessary after this call. |
|||
_ftb: The number of bits to extract. |
|||
This must be between 0 and 25, inclusive. |
|||
Return: The decoded bits.*/ |
|||
opus_uint32 ec_dec_bits(ec_dec *_this,unsigned _ftb); |
|||
|
|||
#endif |
|||
@ -0,0 +1,305 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
Copyright (c) 2008-2009 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. |
|||
*/ |
|||
|
|||
#if defined(HAVE_CONFIG_H) |
|||
# include "config.h" |
|||
#endif |
|||
#include "os_support.h" |
|||
#include "arch.h" |
|||
#include "entenc.h" |
|||
#include "mfrngcod.h" |
|||
|
|||
/*A range encoder.
|
|||
See entdec.c and the references for implementation details \cite{Mar79,MNW98}. |
|||
|
|||
@INPROCEEDINGS{Mar79, |
|||
author="Martin, G.N.N.", |
|||
title="Range encoding: an algorithm for removing redundancy from a digitised |
|||
message", |
|||
booktitle="Video \& Data Recording Conference", |
|||
year=1979, |
|||
address="Southampton", |
|||
month=Jul |
|||
} |
|||
@ARTICLE{MNW98, |
|||
author="Alistair Moffat and Radford Neal and Ian H. Witten", |
|||
title="Arithmetic Coding Revisited", |
|||
journal="{ACM} Transactions on Information Systems", |
|||
year=1998, |
|||
volume=16, |
|||
number=3, |
|||
pages="256--294", |
|||
month=Jul, |
|||
URL="http://www.stanford.edu/class/ee398/handouts/papers/Moffat98ArithmCoding.pdf" |
|||
}*/ |
|||
|
|||
static int ec_write_byte(ec_enc *_this,unsigned _value){ |
|||
if(_this->offs+_this->end_offs>=_this->storage)return -1; |
|||
_this->buf[_this->offs++]=(unsigned char)_value; |
|||
return 0; |
|||
} |
|||
|
|||
static int ec_write_byte_at_end(ec_enc *_this,unsigned _value){ |
|||
if(_this->offs+_this->end_offs>=_this->storage)return -1; |
|||
_this->buf[_this->storage-++(_this->end_offs)]=(unsigned char)_value; |
|||
return 0; |
|||
} |
|||
|
|||
/*Outputs a symbol, with a carry bit.
|
|||
If there is a potential to propagate a carry over several symbols, they are |
|||
buffered until it can be determined whether or not an actual carry will |
|||
occur. |
|||
If the counter for the buffered symbols overflows, then the stream becomes |
|||
undecodable. |
|||
This gives a theoretical limit of a few billion symbols in a single packet on |
|||
32-bit systems. |
|||
The alternative is to truncate the range in order to force a carry, but |
|||
requires similar carry tracking in the decoder, needlessly slowing it down.*/ |
|||
static void ec_enc_carry_out(ec_enc *_this,int _c){ |
|||
if(_c!=EC_SYM_MAX){ |
|||
/*No further carry propagation possible, flush buffer.*/ |
|||
int carry; |
|||
carry=_c>>EC_SYM_BITS; |
|||
/*Don't output a byte on the first write.
|
|||
This compare should be taken care of by branch-prediction thereafter.*/ |
|||
if(_this->rem>=0)_this->error|=ec_write_byte(_this,_this->rem+carry); |
|||
if(_this->ext>0){ |
|||
unsigned sym; |
|||
sym=(EC_SYM_MAX+carry)&EC_SYM_MAX; |
|||
do _this->error|=ec_write_byte(_this,sym); |
|||
while(--(_this->ext)>0); |
|||
} |
|||
_this->rem=_c&EC_SYM_MAX; |
|||
} |
|||
else _this->ext++; |
|||
} |
|||
|
|||
static OPUS_INLINE void ec_enc_normalize(ec_enc *_this){ |
|||
/*If the range is too small, output some bits and rescale it.*/ |
|||
while(_this->rng<=EC_CODE_BOT){ |
|||
ec_enc_carry_out(_this,(int)(_this->val>>EC_CODE_SHIFT)); |
|||
/*Move the next-to-high-order symbol into the high-order position.*/ |
|||
_this->val=(_this->val<<EC_SYM_BITS)&(EC_CODE_TOP-1); |
|||
_this->rng<<=EC_SYM_BITS; |
|||
_this->nbits_total+=EC_SYM_BITS; |
|||
} |
|||
} |
|||
|
|||
void ec_enc_init(ec_enc *_this,unsigned char *_buf,opus_uint32 _size){ |
|||
_this->buf=_buf; |
|||
_this->end_offs=0; |
|||
_this->end_window=0; |
|||
_this->nend_bits=0; |
|||
/*This is the offset from which ec_tell() will subtract partial bits.*/ |
|||
_this->nbits_total=EC_CODE_BITS+1; |
|||
_this->offs=0; |
|||
_this->rng=EC_CODE_TOP; |
|||
_this->rem=-1; |
|||
_this->val=0; |
|||
_this->ext=0; |
|||
_this->storage=_size; |
|||
_this->error=0; |
|||
} |
|||
|
|||
void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft){ |
|||
opus_uint32 r; |
|||
r=celt_udiv(_this->rng,_ft); |
|||
if(_fl>0){ |
|||
_this->val+=_this->rng-IMUL32(r,(_ft-_fl)); |
|||
_this->rng=IMUL32(r,(_fh-_fl)); |
|||
} |
|||
else _this->rng-=IMUL32(r,(_ft-_fh)); |
|||
ec_enc_normalize(_this); |
|||
} |
|||
|
|||
void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _bits){ |
|||
opus_uint32 r; |
|||
r=_this->rng>>_bits; |
|||
if(_fl>0){ |
|||
_this->val+=_this->rng-IMUL32(r,((1U<<_bits)-_fl)); |
|||
_this->rng=IMUL32(r,(_fh-_fl)); |
|||
} |
|||
else _this->rng-=IMUL32(r,((1U<<_bits)-_fh)); |
|||
ec_enc_normalize(_this); |
|||
} |
|||
|
|||
/*The probability of having a "one" is 1/(1<<_logp).*/ |
|||
void ec_enc_bit_logp(ec_enc *_this,int _val,unsigned _logp){ |
|||
opus_uint32 r; |
|||
opus_uint32 s; |
|||
opus_uint32 l; |
|||
r=_this->rng; |
|||
l=_this->val; |
|||
s=r>>_logp; |
|||
r-=s; |
|||
if(_val)_this->val=l+r; |
|||
_this->rng=_val?s:r; |
|||
ec_enc_normalize(_this); |
|||
} |
|||
|
|||
void ec_enc_icdf(ec_enc *_this,int _s,const unsigned char *_icdf,unsigned _ftb){ |
|||
opus_uint32 r; |
|||
r=_this->rng>>_ftb; |
|||
if(_s>0){ |
|||
_this->val+=_this->rng-IMUL32(r,_icdf[_s-1]); |
|||
_this->rng=IMUL32(r,_icdf[_s-1]-_icdf[_s]); |
|||
} |
|||
else _this->rng-=IMUL32(r,_icdf[_s]); |
|||
ec_enc_normalize(_this); |
|||
} |
|||
|
|||
void ec_enc_icdf16(ec_enc *_this,int _s,const opus_uint16 *_icdf,unsigned _ftb){ |
|||
opus_uint32 r; |
|||
r=_this->rng>>_ftb; |
|||
if(_s>0){ |
|||
_this->val+=_this->rng-IMUL32(r,_icdf[_s-1]); |
|||
_this->rng=IMUL32(r,_icdf[_s-1]-_icdf[_s]); |
|||
} |
|||
else _this->rng-=IMUL32(r,_icdf[_s]); |
|||
ec_enc_normalize(_this); |
|||
} |
|||
|
|||
void ec_enc_uint(ec_enc *_this,opus_uint32 _fl,opus_uint32 _ft){ |
|||
unsigned ft; |
|||
unsigned fl; |
|||
int ftb; |
|||
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ |
|||
celt_assert(_ft>1); |
|||
_ft--; |
|||
ftb=EC_ILOG(_ft); |
|||
if(ftb>EC_UINT_BITS){ |
|||
ftb-=EC_UINT_BITS; |
|||
ft=(_ft>>ftb)+1; |
|||
fl=(unsigned)(_fl>>ftb); |
|||
ec_encode(_this,fl,fl+1,ft); |
|||
ec_enc_bits(_this,_fl&(((opus_uint32)1<<ftb)-1U),ftb); |
|||
} |
|||
else ec_encode(_this,_fl,_fl+1,_ft+1); |
|||
} |
|||
|
|||
void ec_enc_bits(ec_enc *_this,opus_uint32 _fl,unsigned _bits){ |
|||
ec_window window; |
|||
int used; |
|||
window=_this->end_window; |
|||
used=_this->nend_bits; |
|||
celt_assert(_bits>0); |
|||
if(used+_bits>EC_WINDOW_SIZE){ |
|||
do{ |
|||
_this->error|=ec_write_byte_at_end(_this,(unsigned)window&EC_SYM_MAX); |
|||
window>>=EC_SYM_BITS; |
|||
used-=EC_SYM_BITS; |
|||
} |
|||
while(used>=EC_SYM_BITS); |
|||
} |
|||
window|=(ec_window)_fl<<used; |
|||
used+=_bits; |
|||
_this->end_window=window; |
|||
_this->nend_bits=used; |
|||
_this->nbits_total+=_bits; |
|||
} |
|||
|
|||
void ec_enc_patch_initial_bits(ec_enc *_this,unsigned _val,unsigned _nbits){ |
|||
int shift; |
|||
unsigned mask; |
|||
celt_assert(_nbits<=EC_SYM_BITS); |
|||
shift=EC_SYM_BITS-_nbits; |
|||
mask=((1<<_nbits)-1)<<shift; |
|||
if(_this->offs>0){ |
|||
/*The first byte has been finalized.*/ |
|||
_this->buf[0]=(unsigned char)((_this->buf[0]&~mask)|_val<<shift); |
|||
} |
|||
else if(_this->rem>=0){ |
|||
/*The first byte is still awaiting carry propagation.*/ |
|||
_this->rem=(_this->rem&~mask)|_val<<shift; |
|||
} |
|||
else if(_this->rng<=(EC_CODE_TOP>>_nbits)){ |
|||
/*The renormalization loop has never been run.*/ |
|||
_this->val=(_this->val&~((opus_uint32)mask<<EC_CODE_SHIFT))| |
|||
(opus_uint32)_val<<(EC_CODE_SHIFT+shift); |
|||
} |
|||
/*The encoder hasn't even encoded _nbits of data yet.*/ |
|||
else _this->error=-1; |
|||
} |
|||
|
|||
void ec_enc_shrink(ec_enc *_this,opus_uint32 _size){ |
|||
celt_assert(_this->offs+_this->end_offs<=_size); |
|||
OPUS_MOVE(_this->buf+_size-_this->end_offs, |
|||
_this->buf+_this->storage-_this->end_offs,_this->end_offs); |
|||
_this->storage=_size; |
|||
} |
|||
|
|||
void ec_enc_done(ec_enc *_this){ |
|||
ec_window window; |
|||
int used; |
|||
opus_uint32 msk; |
|||
opus_uint32 end; |
|||
int l; |
|||
/*We output the minimum number of bits that ensures that the symbols encoded
|
|||
thus far will be decoded correctly regardless of the bits that follow.*/ |
|||
l=EC_CODE_BITS-EC_ILOG(_this->rng); |
|||
msk=(EC_CODE_TOP-1)>>l; |
|||
end=(_this->val+msk)&~msk; |
|||
if((end|msk)>=_this->val+_this->rng){ |
|||
l++; |
|||
msk>>=1; |
|||
end=(_this->val+msk)&~msk; |
|||
} |
|||
while(l>0){ |
|||
ec_enc_carry_out(_this,(int)(end>>EC_CODE_SHIFT)); |
|||
end=(end<<EC_SYM_BITS)&(EC_CODE_TOP-1); |
|||
l-=EC_SYM_BITS; |
|||
} |
|||
/*If we have a buffered byte flush it into the output buffer.*/ |
|||
if(_this->rem>=0||_this->ext>0)ec_enc_carry_out(_this,0); |
|||
/*If we have buffered extra bits, flush them as well.*/ |
|||
window=_this->end_window; |
|||
used=_this->nend_bits; |
|||
while(used>=EC_SYM_BITS){ |
|||
_this->error|=ec_write_byte_at_end(_this,(unsigned)window&EC_SYM_MAX); |
|||
window>>=EC_SYM_BITS; |
|||
used-=EC_SYM_BITS; |
|||
} |
|||
/*Clear any excess space and add any remaining extra bits to the last byte.*/ |
|||
if(!_this->error){ |
|||
OPUS_CLEAR(_this->buf+_this->offs, |
|||
_this->storage-_this->offs-_this->end_offs); |
|||
if(used>0){ |
|||
/*If there's no range coder data at all, give up.*/ |
|||
if(_this->end_offs>=_this->storage)_this->error=-1; |
|||
else{ |
|||
l=-l; |
|||
/*If we've busted, don't add too many extra bits to the last byte; it
|
|||
would corrupt the range coder data, and that's more important.*/ |
|||
if(_this->offs+_this->end_offs>=_this->storage&&l<used){ |
|||
window&=(1<<l)-1; |
|||
_this->error=-1; |
|||
} |
|||
_this->buf[_this->storage-_this->end_offs-1]|=(unsigned char)window; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
|||
Copyright (c) 2008-2009 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. |
|||
*/ |
|||
|
|||
#if !defined(_entenc_H) |
|||
# define _entenc_H (1) |
|||
# include <stddef.h> |
|||
# include "entcode.h" |
|||
|
|||
/*Initializes the encoder.
|
|||
_buf: The buffer to store output bytes in. |
|||
_size: The size of the buffer, in chars.*/ |
|||
void ec_enc_init(ec_enc *_this,unsigned char *_buf,opus_uint32 _size); |
|||
/*Encodes a symbol given its frequency information.
|
|||
The frequency information must be discernable by the decoder, assuming it |
|||
has read only the previous symbols from the stream. |
|||
It is allowable to change the frequency information, or even the entire |
|||
source alphabet, so long as the decoder can tell from the context of the |
|||
previously encoded information that it is supposed to do so as well. |
|||
_fl: The cumulative frequency of all symbols that come before the one to be |
|||
encoded. |
|||
_fh: The cumulative frequency of all symbols up to and including the one to |
|||
be encoded. |
|||
Together with _fl, this defines the range [_fl,_fh) in which the |
|||
decoded value will fall. |
|||
_ft: The sum of the frequencies of all the symbols*/ |
|||
void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft); |
|||
|
|||
/*Equivalent to ec_encode() with _ft==1<<_bits.*/ |
|||
void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _bits); |
|||
|
|||
/* Encode a bit that has a 1/(1<<_logp) probability of being a one */ |
|||
void ec_enc_bit_logp(ec_enc *_this,int _val,unsigned _logp); |
|||
|
|||
/*Encodes a symbol given an "inverse" CDF table.
|
|||
_s: The index of the symbol to encode. |
|||
_icdf: The "inverse" CDF, such that symbol _s falls in the range |
|||
[_s>0?ft-_icdf[_s-1]:0,ft-_icdf[_s]), where ft=1<<_ftb. |
|||
The values must be monotonically non-increasing, and the last value |
|||
must be 0. |
|||
_ftb: The number of bits of precision in the cumulative distribution.*/ |
|||
void ec_enc_icdf(ec_enc *_this,int _s,const unsigned char *_icdf,unsigned _ftb); |
|||
|
|||
/*Encodes a symbol given an "inverse" CDF table.
|
|||
_s: The index of the symbol to encode. |
|||
_icdf: The "inverse" CDF, such that symbol _s falls in the range |
|||
[_s>0?ft-_icdf[_s-1]:0,ft-_icdf[_s]), where ft=1<<_ftb. |
|||
The values must be monotonically non-increasing, and the last value |
|||
must be 0. |
|||
_ftb: The number of bits of precision in the cumulative distribution.*/ |
|||
void ec_enc_icdf16(ec_enc *_this,int _s,const opus_uint16 *_icdf,unsigned _ftb); |
|||
|
|||
/*Encodes a raw unsigned integer in the stream.
|
|||
_fl: The integer to encode. |
|||
_ft: The number of integers that can be encoded (one more than the max). |
|||
This must be at least 2, and no more than 2**32-1.*/ |
|||
void ec_enc_uint(ec_enc *_this,opus_uint32 _fl,opus_uint32 _ft); |
|||
|
|||
/*Encodes a sequence of raw bits in the stream.
|
|||
_fl: The bits to encode. |
|||
_ftb: The number of bits to encode. |
|||
This must be between 1 and 25, inclusive.*/ |
|||
void ec_enc_bits(ec_enc *_this,opus_uint32 _fl,unsigned _ftb); |
|||
|
|||
/*Overwrites a few bits at the very start of an existing stream, after they
|
|||
have already been encoded. |
|||
This makes it possible to have a few flags up front, where it is easy for |
|||
decoders to access them without parsing the whole stream, even if their |
|||
values are not determined until late in the encoding process, without having |
|||
to buffer all the intermediate symbols in the encoder. |
|||
In order for this to work, at least _nbits bits must have already been |
|||
encoded using probabilities that are an exact power of two. |
|||
The encoder can verify the number of encoded bits is sufficient, but cannot |
|||
check this latter condition. |
|||
_val: The bits to encode (in the least _nbits significant bits). |
|||
They will be decoded in order from most-significant to least. |
|||
_nbits: The number of bits to overwrite. |
|||
This must be no more than 8.*/ |
|||
void ec_enc_patch_initial_bits(ec_enc *_this,unsigned _val,unsigned _nbits); |
|||
|
|||
/*Compacts the data to fit in the target size.
|
|||
This moves up the raw bits at the end of the current buffer so they are at |
|||
the end of the new buffer size. |
|||
The caller must ensure that the amount of data that's already been written |
|||
will fit in the new size. |
|||
_size: The number of bytes in the new buffer. |
|||
This must be large enough to contain the bits already written, and |
|||
must be no larger than the existing size.*/ |
|||
void ec_enc_shrink(ec_enc *_this,opus_uint32 _size); |
|||
|
|||
/*Indicates that there are no more symbols to encode.
|
|||
All reamining output bytes are flushed to the output buffer. |
|||
ec_enc_init() must be called before the encoder can be used again.*/ |
|||
void ec_enc_done(ec_enc *_this); |
|||
|
|||
#endif |
|||
@ -0,0 +1,836 @@ |
|||
/* Copyright (C) 2003-2008 Jean-Marc Valin
|
|||
Copyright (C) 2007-2012 Xiph.Org Foundation */ |
|||
/**
|
|||
@file fixed_debug.h |
|||
@brief Fixed-point operations with debugging |
|||
*/ |
|||
/*
|
|||
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_DEBUG_H |
|||
#define FIXED_DEBUG_H |
|||
|
|||
#include <stdio.h> |
|||
#include "opus_defines.h" |
|||
|
|||
#ifdef CELT_C |
|||
OPUS_EXPORT opus_int64 celt_mips=0; |
|||
#else |
|||
extern opus_int64 celt_mips; |
|||
#endif |
|||
|
|||
#define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) |
|||
#define MULT32_32_Q31(a,b) ADD32(ADD32(SHL32(MULT16_16(SHR32((a),16),SHR((b),16)),1), SHR32(MULT16_16SU(SHR32((a),16),((b)&0x0000ffff)),15)), SHR32(MULT16_16SU(SHR32((b),16),((a)&0x0000ffff)),15)) |
|||
|
|||
/** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ |
|||
#define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR32((b),16)), SHR32(MULT16_16SU((a),((b)&0x0000ffff)),16)) |
|||
|
|||
#define MULT16_32_P16(a,b) MULT16_32_PX(a,b,16) |
|||
|
|||
#define QCONST16(x,bits) ((opus_val16)(.5+(x)*(((opus_val32)1)<<(bits)))) |
|||
#define QCONST32(x,bits) ((opus_val32)(.5+(x)*(((opus_val32)1)<<(bits)))) |
|||
|
|||
#define VERIFY_SHORT(x) ((x)<=32767&&(x)>=-32768) |
|||
#define VERIFY_INT(x) ((x)<=2147483647LL&&(x)>=-2147483648LL) |
|||
#define VERIFY_UINT(x) ((x)<=(2147483647LLU<<1)) |
|||
|
|||
#define SHR(a,b) SHR32(a,b) |
|||
#define PSHR(a,b) PSHR32(a,b) |
|||
|
|||
/** Add two 32-bit values, ignore any overflows */ |
|||
#define ADD32_ovflw(a,b) (celt_mips+=2,(opus_val32)((opus_uint32)(a)+(opus_uint32)(b))) |
|||
/** Subtract two 32-bit values, ignore any overflows */ |
|||
#define SUB32_ovflw(a,b) (celt_mips+=2,(opus_val32)((opus_uint32)(a)-(opus_uint32)(b))) |
|||
/* Avoid MSVC warning C4146: unary minus operator applied to unsigned type */ |
|||
/** Negate 32-bit value, ignore any overflows */ |
|||
#define NEG32_ovflw(a) (celt_mips+=2,(opus_val32)(0-(opus_uint32)(a))) |
|||
|
|||
static OPUS_INLINE short NEG16(int x) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(x)) |
|||
{ |
|||
fprintf (stderr, "NEG16: input is not short: %d\n", (int)x); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = -x; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "NEG16: output is not short: %d\n", (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
static OPUS_INLINE int NEG32(opus_int64 x) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(x)) |
|||
{ |
|||
fprintf (stderr, "NEG16: input is not int: %d\n", (int)x); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = -x; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "NEG16: output is not int: %d\n", (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#define EXTRACT16(x) EXTRACT16_(x, __FILE__, __LINE__) |
|||
static OPUS_INLINE short EXTRACT16_(int x, char *file, int line) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(x)) |
|||
{ |
|||
fprintf (stderr, "EXTRACT16: input is not short: %d in %s: line %d\n", x, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = x; |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
#define EXTEND32(x) EXTEND32_(x, __FILE__, __LINE__) |
|||
static OPUS_INLINE int EXTEND32_(int x, char *file, int line) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(x)) |
|||
{ |
|||
fprintf (stderr, "EXTEND32: input is not short: %d in %s: line %d\n", x, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = x; |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
#define SHR16(a, shift) SHR16_(a, shift, __FILE__, __LINE__) |
|||
static OPUS_INLINE short SHR16_(int a, int shift, char *file, int line) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) |
|||
{ |
|||
fprintf (stderr, "SHR16: inputs are not short: %d >> %d in %s: line %d\n", a, shift, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a>>shift; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "SHR16: output is not short: %d in %s: line %d\n", res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
#define SHL16(a, shift) SHL16_(a, shift, __FILE__, __LINE__) |
|||
static OPUS_INLINE short SHL16_(int a, int shift, char *file, int line) |
|||
{ |
|||
opus_int32 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) |
|||
{ |
|||
fprintf (stderr, "SHL16: inputs are not short: %d %d in %s: line %d\n", a, shift, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = (opus_int32)((opus_uint32)a<<shift); |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "SHL16: output is not short: %d in %s: line %d\n", res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
static OPUS_INLINE int SHR32(opus_int64 a, int shift) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) |
|||
{ |
|||
fprintf (stderr, "SHR32: inputs are not int: %d %d\n", (int)a, shift); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a>>shift; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "SHR32: output is not int: %d\n", (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
#define SHL32(a, shift) SHL32_(a, shift, __FILE__, __LINE__) |
|||
static OPUS_INLINE int SHL32_(opus_int64 a, int shift, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) |
|||
{ |
|||
fprintf (stderr, "SHL32: inputs are not int: %lld %d in %s: line %d\n", (long long)a, shift, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = (opus_int64)((opus_uint64)a<<shift); |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "SHL32: output is not int: %lld<<%d = %lld in %s: line %d\n", (long long)a, shift, (long long)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#define PSHR32(a,shift) (celt_mips--,SHR32(ADD32((a),(((opus_val32)(1)<<((shift))>>1))),shift)) |
|||
#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) |
|||
|
|||
#define ROUND16(x,a) (celt_mips--,EXTRACT16(PSHR32((x),(a)))) |
|||
#define SROUND16(x,a) (celt_mips--,EXTRACT16(SATURATE(PSHR32(x,a), 32767))); |
|||
|
|||
#define HALF16(x) (SHR16(x,1)) |
|||
#define HALF32(x) (SHR32(x,1)) |
|||
|
|||
#define ADD16(a, b) ADD16_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE short ADD16_(int a, int b, char *file, int line) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "ADD16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a+b; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "ADD16: output is not short: %d+%d=%d in %s: line %d\n", a,b,res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
#define SUB16(a, b) SUB16_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE short SUB16_(int a, int b, char *file, int line) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "SUB16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a-b; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "SUB16: output is not short: %d in %s: line %d\n", res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
#define ADD32(a, b) ADD32_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE int ADD32_(opus_int64 a, opus_int64 b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "ADD32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a+b; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "ADD32: output is not int: %d in %s: line %d\n", (int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#define SUB32(a, b) SUB32_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE int SUB32_(opus_int64 a, opus_int64 b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "SUB32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a-b; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "SUB32: output is not int: %d in %s: line %d\n", (int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#undef UADD32 |
|||
#define UADD32(a, b) UADD32_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE unsigned int UADD32_(opus_uint64 a, opus_uint64 b, char *file, int line) |
|||
{ |
|||
opus_uint64 res; |
|||
if (!VERIFY_UINT(a) || !VERIFY_UINT(b)) |
|||
{ |
|||
fprintf (stderr, "UADD32: inputs are not uint32: %llu %llu in %s: line %d\n", (unsigned long long)a, (unsigned long long)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a+b; |
|||
if (!VERIFY_UINT(res)) |
|||
{ |
|||
fprintf (stderr, "UADD32: output is not uint32: %llu in %s: line %d\n", (unsigned long long)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#undef USUB32 |
|||
#define USUB32(a, b) USUB32_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE unsigned int USUB32_(opus_uint64 a, opus_uint64 b, char *file, int line) |
|||
{ |
|||
opus_uint64 res; |
|||
if (!VERIFY_UINT(a) || !VERIFY_UINT(b)) |
|||
{ |
|||
fprintf (stderr, "USUB32: inputs are not uint32: %llu %llu in %s: line %d\n", (unsigned long long)a, (unsigned long long)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
if (a<b) |
|||
{ |
|||
fprintf (stderr, "USUB32: inputs underflow: %llu < %llu in %s: line %d\n", (unsigned long long)a, (unsigned long long)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a-b; |
|||
if (!VERIFY_UINT(res)) |
|||
{ |
|||
fprintf (stderr, "USUB32: output is not uint32: %llu - %llu = %llu in %s: line %d\n", (unsigned long long)a, (unsigned long long)b, (unsigned long long)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
/* result fits in 16 bits */ |
|||
static OPUS_INLINE short MULT16_16_16(int a, int b) |
|||
{ |
|||
int res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_16: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a*b; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_16: output is not short: %d\n", res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
/* result fits in 32 bits */ |
|||
static OPUS_INLINE int MULT32_32_32(opus_int64 a, opus_int64 b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT32_32_32: inputs are not int: %lld %lld\n", (long long)a, (long long)b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a*b; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT32_32_32: output is not int: %lld\n", (long long)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=5; |
|||
return res; |
|||
} |
|||
|
|||
static OPUS_INLINE int MULT32_32_Q16(opus_int64 a, opus_int64 b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_INT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT32_32_Q16: inputs are not int: %lld %lld\n", (long long)a, (long long)b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)(a)*(opus_int64)(b)) >> 16; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT32_32_Q16: output is not int: %lld*%lld=%lld\n", (long long)a, (long long)b, (long long)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=5; |
|||
return res; |
|||
} |
|||
|
|||
#define MULT16_16(a, b) MULT16_16_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE int MULT16_16_(int a, int b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16: output is not int: %d in %s: line %d\n", (int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips++; |
|||
return res; |
|||
} |
|||
|
|||
#define MAC16_16(c,a,b) (celt_mips-=2,ADD32((c),MULT16_16((a),(b)))) |
|||
|
|||
#define MULT16_32_QX(a, b, Q) MULT16_32_QX_(a, b, Q, __FILE__, __LINE__) |
|||
static OPUS_INLINE int MULT16_32_QX_(int a, opus_int64 b, int Q, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_Q%d: inputs are not short+int: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
if (ABS32(b)>=((opus_int64)(1)<<(16+Q))) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = (((opus_int64)a)*(opus_int64)b) >> Q; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_Q%d: output is not int: %d*%d=%d in %s: line %d\n", Q, (int)a, (int)b,(int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
if (Q==15) |
|||
celt_mips+=3; |
|||
else |
|||
celt_mips+=4; |
|||
return res; |
|||
} |
|||
|
|||
#define MULT16_32_PX(a, b, Q) MULT16_32_PX_(a, b, Q, __FILE__, __LINE__) |
|||
static OPUS_INLINE int MULT16_32_PX_(int a, opus_int64 b, int Q, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d in %s: line %d\n\n", Q, (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
if (ABS32(b)>=((opus_int64)(1)<<(16+Q))) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n\n", Q, (int)a, (int)b,file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((((opus_int64)a)*(opus_int64)b) + (((opus_val32)(1)<<Q)>>1))>> Q; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d in %s: line %d\n\n", Q, (int)a, (int)b,(int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
if (Q==15) |
|||
celt_mips+=4; |
|||
else |
|||
celt_mips+=5; |
|||
return res; |
|||
} |
|||
|
|||
#define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15) |
|||
#define MAC16_32_Q15(c,a,b) (celt_mips-=2,ADD32((c),MULT16_32_Q15((a),(b)))) |
|||
#define MAC16_32_Q16(c,a,b) (celt_mips-=2,ADD32((c),MULT16_32_Q16((a),(b)))) |
|||
|
|||
static OPUS_INLINE int SATURATE(int a, int b) |
|||
{ |
|||
if (a>b) |
|||
a=b; |
|||
if (a<-b) |
|||
a = -b; |
|||
celt_mips+=3; |
|||
return a; |
|||
} |
|||
|
|||
static OPUS_INLINE opus_int16 SATURATE16(opus_int32 a) |
|||
{ |
|||
celt_mips+=3; |
|||
if (a>32767) |
|||
return 32767; |
|||
else if (a<-32768) |
|||
return -32768; |
|||
else return a; |
|||
} |
|||
|
|||
static OPUS_INLINE int MULT16_16_Q11_32(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q11: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res >>= 11; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q11: output is not short: %d*%d=%d\n", (int)a, (int)b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=3; |
|||
return res; |
|||
} |
|||
static OPUS_INLINE short MULT16_16_Q13(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q13: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res >>= 13; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q13: output is not short: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=3; |
|||
return res; |
|||
} |
|||
static OPUS_INLINE short MULT16_16_Q14(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q14: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res >>= 14; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q14: output is not short: %d\n", (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=3; |
|||
return res; |
|||
} |
|||
|
|||
#define MULT16_16_Q15(a, b) MULT16_16_Q15_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE short MULT16_16_Q15_(int a, int b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q15: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res >>= 15; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_Q15: output is not short: %d in %s: line %d\n", (int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=1; |
|||
return res; |
|||
} |
|||
|
|||
static OPUS_INLINE short MULT16_16_P13(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P13: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res += 4096; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P13: overflow: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res >>= 13; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P13: output is not short: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=4; |
|||
return res; |
|||
} |
|||
static OPUS_INLINE short MULT16_16_P14(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P14: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res += 8192; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P14: overflow: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res >>= 14; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P14: output is not short: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=4; |
|||
return res; |
|||
} |
|||
static OPUS_INLINE short MULT16_16_P15(int a, int b) |
|||
{ |
|||
opus_int64 res; |
|||
if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P15: inputs are not short: %d %d\n", a, b); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = ((opus_int64)a)*b; |
|||
res += 16384; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P15: overflow: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res >>= 15; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "MULT16_16_P15: output is not short: %d*%d=%d\n", a, b, (int)res); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=2; |
|||
return res; |
|||
} |
|||
|
|||
#define DIV32_16(a, b) DIV32_16_(a, b, __FILE__, __LINE__) |
|||
|
|||
static OPUS_INLINE int DIV32_16_(opus_int64 a, opus_int64 b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (b==0) |
|||
{ |
|||
fprintf(stderr, "DIV32_16: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
return 0; |
|||
} |
|||
if (!VERIFY_INT(a) || !VERIFY_SHORT(b)) |
|||
{ |
|||
fprintf (stderr, "DIV32_16: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a/b; |
|||
if (!VERIFY_SHORT(res)) |
|||
{ |
|||
fprintf (stderr, "DIV32_16: output is not short: %d / %d = %d in %s: line %d\n", (int)a,(int)b,(int)res, file, line); |
|||
if (res>32767) |
|||
res = 32767; |
|||
if (res<-32768) |
|||
res = -32768; |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=35; |
|||
return res; |
|||
} |
|||
|
|||
#define DIV32(a, b) DIV32_(a, b, __FILE__, __LINE__) |
|||
static OPUS_INLINE int DIV32_(opus_int64 a, opus_int64 b, char *file, int line) |
|||
{ |
|||
opus_int64 res; |
|||
if (b==0) |
|||
{ |
|||
fprintf(stderr, "DIV32: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
if (!VERIFY_INT(a) || !VERIFY_INT(b)) |
|||
{ |
|||
fprintf (stderr, "DIV32: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
res = a/b; |
|||
if (!VERIFY_INT(res)) |
|||
{ |
|||
fprintf (stderr, "DIV32: output is not int: %d in %s: line %d\n", (int)res, file, line); |
|||
#ifdef FIXED_DEBUG_ASSERT |
|||
celt_assert(0); |
|||
#endif |
|||
} |
|||
celt_mips+=70; |
|||
return res; |
|||
} |
|||
|
|||
static OPUS_INLINE opus_val16 SIG2WORD16_generic(celt_sig x) |
|||
{ |
|||
x = PSHR32(x, SIG_SHIFT); |
|||
x = MAX32(x, -32768); |
|||
x = MIN32(x, 32767); |
|||
return EXTRACT16(x); |
|||
} |
|||
#define SIG2WORD16(x) (SIG2WORD16_generic(x)) |
|||
|
|||
|
|||
#undef PRINT_MIPS |
|||
#define PRINT_MIPS(file) do {fprintf (file, "total complexity = %llu MIPS\n", (unsigned long long)celt_mips);} while (0); |
|||
|
|||
#endif |
|||
@ -0,0 +1,188 @@ |
|||
/* Copyright (C) 2007-2009 Xiph.Org Foundation
|
|||
Copyright (C) 2003-2008 Jean-Marc Valin |
|||
Copyright (C) 2007-2008 CSIRO */ |
|||
/**
|
|||
@file fixed_generic.h |
|||
@brief Generic fixed-point operations |
|||
*/ |
|||
/*
|
|||
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_GENERIC_H |
|||
#define FIXED_GENERIC_H |
|||
|
|||
/** Multiply a 16-bit signed value by a 16-bit unsigned value. The result is a 32-bit signed value */ |
|||
#define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) |
|||
|
|||
/** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ |
|||
#if OPUS_FAST_INT64 |
|||
#define MULT16_32_Q16(a,b) ((opus_val32)SHR((opus_int64)((opus_val16)(a))*(b),16)) |
|||
#else |
|||
#define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) |
|||
#endif |
|||
|
|||
/** 16x32 multiplication, followed by a 16-bit shift right (round-to-nearest). Results fits in 32 bits */ |
|||
#if OPUS_FAST_INT64 |
|||
#define MULT16_32_P16(a,b) ((opus_val32)PSHR((opus_int64)((opus_val16)(a))*(b),16)) |
|||
#else |
|||
#define MULT16_32_P16(a,b) ADD32(MULT16_16((a),SHR((b),16)), PSHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) |
|||
#endif |
|||
|
|||
/** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ |
|||
#if OPUS_FAST_INT64 |
|||
#define MULT16_32_Q15(a,b) ((opus_val32)SHR((opus_int64)((opus_val16)(a))*(b),15)) |
|||
#else |
|||
#define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15)) |
|||
#endif |
|||
|
|||
/** 32x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ |
|||
#if OPUS_FAST_INT64 |
|||
#define MULT32_32_Q16(a,b) ((opus_val32)SHR((opus_int64)(a)*(opus_int64)(b),16)) |
|||
#else |
|||
#define MULT32_32_Q16(a,b) (ADD32(ADD32(ADD32((opus_val32)(SHR32(((opus_uint32)((a)&0x0000ffff)*(opus_uint32)((b)&0x0000ffff)),16)), MULT16_16SU(SHR32(a,16),((b)&0x0000ffff))), MULT16_16SU(SHR32(b,16),((a)&0x0000ffff))), SHL32(MULT16_16(SHR32(a,16),SHR32(b,16)),16))) |
|||
#endif |
|||
|
|||
/** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ |
|||
#if OPUS_FAST_INT64 |
|||
#define MULT32_32_Q31(a,b) ((opus_val32)SHR((opus_int64)(a)*(opus_int64)(b),31)) |
|||
#else |
|||
#define MULT32_32_Q31(a,b) ADD32(ADD32(SHL(MULT16_16(SHR((a),16),SHR((b),16)),1), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),15)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),15)) |
|||
#endif |
|||
|
|||
/** Compile-time conversion of float constant to 16-bit value */ |
|||
#define QCONST16(x,bits) ((opus_val16)(.5+(x)*(((opus_val32)1)<<(bits)))) |
|||
|
|||
/** Compile-time conversion of float constant to 32-bit value */ |
|||
#define QCONST32(x,bits) ((opus_val32)(.5+(x)*(((opus_val32)1)<<(bits)))) |
|||
|
|||
/** Negate a 16-bit value */ |
|||
#define NEG16(x) (-(x)) |
|||
/** Negate a 32-bit value */ |
|||
#define NEG32(x) (-(x)) |
|||
|
|||
/** Change a 32-bit value into a 16-bit value. The value is assumed to fit in 16-bit, otherwise the result is undefined */ |
|||
#define EXTRACT16(x) ((opus_val16)(x)) |
|||
/** Change a 16-bit value into a 32-bit value */ |
|||
#define EXTEND32(x) ((opus_val32)(x)) |
|||
|
|||
/** Arithmetic shift-right of a 16-bit value */ |
|||
#define SHR16(a,shift) ((a) >> (shift)) |
|||
/** Arithmetic shift-left of a 16-bit value */ |
|||
#define SHL16(a,shift) ((opus_int16)((opus_uint16)(a)<<(shift))) |
|||
/** Arithmetic shift-right of a 32-bit value */ |
|||
#define SHR32(a,shift) ((a) >> (shift)) |
|||
/** Arithmetic shift-left of a 32-bit value */ |
|||
#define SHL32(a,shift) ((opus_int32)((opus_uint32)(a)<<(shift))) |
|||
|
|||
/** 32-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ |
|||
#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) |
|||
/** 32-bit arithmetic shift right where the argument can be negative */ |
|||
#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) |
|||
|
|||
/** "RAW" macros, should not be used outside of this header file */ |
|||
#define SHR(a,shift) ((a) >> (shift)) |
|||
#define SHL(a,shift) SHL32(a,shift) |
|||
#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) |
|||
#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) |
|||
|
|||
#define SATURATE16(x) (EXTRACT16((x)>32767 ? 32767 : (x)<-32768 ? -32768 : (x))) |
|||
|
|||
/** Shift by a and round-to-nearest 32-bit value. Result is a 16-bit value */ |
|||
#define ROUND16(x,a) (EXTRACT16(PSHR32((x),(a)))) |
|||
/** Shift by a and round-to-nearest 32-bit value. Result is a saturated 16-bit value */ |
|||
#define SROUND16(x,a) EXTRACT16(SATURATE(PSHR32(x,a), 32767)); |
|||
|
|||
/** Divide by two */ |
|||
#define HALF16(x) (SHR16(x,1)) |
|||
#define HALF32(x) (SHR32(x,1)) |
|||
|
|||
/** Add two 16-bit values */ |
|||
#define ADD16(a,b) ((opus_val16)((opus_val16)(a)+(opus_val16)(b))) |
|||
/** Subtract two 16-bit values */ |
|||
#define SUB16(a,b) ((opus_val16)(a)-(opus_val16)(b)) |
|||
/** Add two 32-bit values */ |
|||
#define ADD32(a,b) ((opus_val32)(a)+(opus_val32)(b)) |
|||
/** Subtract two 32-bit values */ |
|||
#define SUB32(a,b) ((opus_val32)(a)-(opus_val32)(b)) |
|||
|
|||
/** Add two 32-bit values, ignore any overflows */ |
|||
#define ADD32_ovflw(a,b) ((opus_val32)((opus_uint32)(a)+(opus_uint32)(b))) |
|||
/** Subtract two 32-bit values, ignore any overflows */ |
|||
#define SUB32_ovflw(a,b) ((opus_val32)((opus_uint32)(a)-(opus_uint32)(b))) |
|||
/* Avoid MSVC warning C4146: unary minus operator applied to unsigned type */ |
|||
/** Negate 32-bit value, ignore any overflows */ |
|||
#define NEG32_ovflw(a) ((opus_val32)(0-(opus_uint32)(a))) |
|||
|
|||
/** 16x16 multiplication where the result fits in 16 bits */ |
|||
#define MULT16_16_16(a,b) ((((opus_val16)(a))*((opus_val16)(b)))) |
|||
|
|||
/** 32x32 multiplication where the result fits in 32 bits */ |
|||
#define MULT32_32_32(a,b) ((((opus_val32)(a))*((opus_val32)(b)))) |
|||
|
|||
/* (opus_val32)(opus_val16) gives TI compiler a hint that it's 16x16->32 multiply */ |
|||
/** 16x16 multiplication where the result fits in 32 bits */ |
|||
#define MULT16_16(a,b) (((opus_val32)(opus_val16)(a))*((opus_val32)(opus_val16)(b))) |
|||
|
|||
/** 16x16 multiply-add where the result fits in 32 bits */ |
|||
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((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. */ |
|||
#define MAC16_32_Q15(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) |
|||
|
|||
/** 16x32 multiplication, followed by a 16-bit shift right and 32-bit add.
|
|||
Results fits in 32 bits */ |
|||
#define MAC16_32_Q16(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16))) |
|||
|
|||
#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) |
|||
#define MULT16_16_Q11(a,b) (SHR(MULT16_16((a),(b)),11)) |
|||
#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) |
|||
#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) |
|||
#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) |
|||
|
|||
#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) |
|||
#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) |
|||
#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) |
|||
|
|||
/** Divide a 32-bit value by a 16-bit value. Result fits in 16 bits */ |
|||
#define DIV32_16(a,b) ((opus_val16)(((opus_val32)(a))/((opus_val16)(b)))) |
|||
|
|||
/** Divide a 32-bit value by a 32-bit value. Result fits in 32 bits */ |
|||
#define DIV32(a,b) (((opus_val32)(a))/((opus_val32)(b))) |
|||
|
|||
#if defined(MIPSr1_ASM) |
|||
#include "mips/fixed_generic_mipsr1.h" |
|||
#endif |
|||
|
|||
static OPUS_INLINE opus_val16 SIG2WORD16_generic(celt_sig x) |
|||
{ |
|||
x = PSHR32(x, SIG_SHIFT); |
|||
x = MAX32(x, -32768); |
|||
x = MIN32(x, 32767); |
|||
return EXTRACT16(x); |
|||
} |
|||
#define SIG2WORD16(x) (SIG2WORD16_generic(x)) |
|||
|
|||
#endif |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue