569 changed files with 689839 additions and 1332 deletions
@ -1,12 +1,13 @@ |
|||||
{ |
{ |
||||
"mcpServers": { |
"mcpServers": { |
||||
|
"deepsound-mcp": { |
||||
"amap-amap-sse": { |
"url": "https://api.deapsound.com/sse" |
||||
"url": "https://mcp.amap.com/sse?key=e5fdc9605eabdeb5626f18f5721f343d" |
}, |
||||
|
"voitrans-mcp": { |
||||
|
"url": "https://www.voitrans.net/sse" |
||||
}, |
}, |
||||
"web-search": { |
"web-search": { |
||||
"url": "http://mcp.ideapsound.com:8000/sse" |
"url": "http://mcp.ideapsound.com:8000/sse" |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
} |
} |
||||
@ -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,30 @@ |
|||||
|
import 'package:json_annotation/json_annotation.dart'; |
||||
|
|
||||
|
part 'appconfig_model.g.dart'; // 生成的文件名 |
||||
|
|
||||
|
@JsonSerializable() |
||||
|
class UserGetAppConfigResp { |
||||
|
final Map<String, String> env; |
||||
|
final Map<String, DBMCPServer> mcps; |
||||
|
UserGetAppConfigResp({required this.env, required this.mcps}); |
||||
|
|
||||
|
factory UserGetAppConfigResp.fromJson(Map<String, dynamic> json) => |
||||
|
_$UserGetAppConfigRespFromJson(json); |
||||
|
Map<String, dynamic> toJson() => _$UserGetAppConfigRespToJson(this); |
||||
|
} |
||||
|
|
||||
|
@JsonSerializable() |
||||
|
class DBMCPServer { |
||||
|
final String servername; |
||||
|
final String url; |
||||
|
final List<String>? tools; |
||||
|
DBMCPServer({ |
||||
|
required this.servername, |
||||
|
required this.url, |
||||
|
required this.tools, |
||||
|
}); |
||||
|
|
||||
|
factory DBMCPServer.fromJson(Map<String, dynamic> json) => |
||||
|
_$DBMCPServerFromJson(json); |
||||
|
Map<String, dynamic> toJson() => _$DBMCPServerToJson(this); |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||
|
|
||||
|
part of 'appconfig_model.dart'; |
||||
|
|
||||
|
// ************************************************************************** |
||||
|
// JsonSerializableGenerator |
||||
|
// ************************************************************************** |
||||
|
|
||||
|
UserGetAppConfigResp _$UserGetAppConfigRespFromJson( |
||||
|
Map<String, dynamic> json) => |
||||
|
UserGetAppConfigResp( |
||||
|
env: Map<String, String>.from(json['env'] as Map), |
||||
|
mcps: (json['mcps'] as Map<String, dynamic>).map( |
||||
|
(k, e) => MapEntry(k, DBMCPServer.fromJson(e as Map<String, dynamic>)), |
||||
|
), |
||||
|
); |
||||
|
|
||||
|
Map<String, dynamic> _$UserGetAppConfigRespToJson( |
||||
|
UserGetAppConfigResp instance) => |
||||
|
<String, dynamic>{ |
||||
|
'env': instance.env, |
||||
|
'mcps': instance.mcps, |
||||
|
}; |
||||
|
|
||||
|
DBMCPServer _$DBMCPServerFromJson(Map<String, dynamic> json) => DBMCPServer( |
||||
|
servername: json['servername'] as String, |
||||
|
url: json['url'] as String, |
||||
|
tools: |
||||
|
(json['tools'] as List<dynamic>?)?.map((e) => e as String).toList(), |
||||
|
); |
||||
|
|
||||
|
Map<String, dynamic> _$DBMCPServerToJson(DBMCPServer instance) => |
||||
|
<String, dynamic>{ |
||||
|
'servername': instance.servername, |
||||
|
'url': instance.url, |
||||
|
'tools': instance.tools, |
||||
|
}; |
||||
@ -0,0 +1,208 @@ |
|||||
|
import 'dart:async'; |
||||
|
import 'dart:io'; |
||||
|
import 'dart:convert'; |
||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart'; |
||||
|
import 'package:get_storage/get_storage.dart'; |
||||
|
import 'package:spotify_sdk/spotify_sdk.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'ai_service.dart'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
import 'package:path_provider/path_provider.dart'; |
||||
|
|
||||
|
/// OpenAI服务适配器 - 连接AiService接口与OpenAIService插件 |
||||
|
class SpotifService { |
||||
|
bool isInitialized = false; |
||||
|
final GetStorage _storage = GetStorage(); |
||||
|
|
||||
|
late String SPOTIFY_CLIENT_ID; |
||||
|
late String SPOTIFY_REDIRECT_URL; |
||||
|
|
||||
|
/// 初始化OpenAI服务 |
||||
|
Future<bool> initialize() async { |
||||
|
if (!isInitialized) { |
||||
|
final _env = _storage.read("ENV") as Map<String, String>; |
||||
|
SPOTIFY_CLIENT_ID = _env['SPOTIFY_CLIENT_ID'] ?? ''; |
||||
|
SPOTIFY_REDIRECT_URL = _env['SPOTIFY_REDIRECT_URL'] ?? ''; |
||||
|
printInfo( |
||||
|
info: |
||||
|
"SpotifService----------- initialize SPOTIFY_CLIENT_ID:${SPOTIFY_CLIENT_ID} SPOTIFY_REDIRECT_URL:${SPOTIFY_REDIRECT_URL}"); |
||||
|
|
||||
|
await getAccessToken(); |
||||
|
await connectToSpotifyRemote(); |
||||
|
isInitialized = true; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// 获取Spotify SDK的版本信息 |
||||
|
Future<void> connectToSpotifyRemote() async { |
||||
|
try { |
||||
|
var result = await SpotifySdk.connectToSpotifyRemote( |
||||
|
clientId: SPOTIFY_CLIENT_ID, |
||||
|
redirectUrl: SPOTIFY_REDIRECT_URL, |
||||
|
scope: |
||||
|
'user-read-private user-read-email streaming app-remote-control user-modify-playback-state', |
||||
|
); |
||||
|
printInfo( |
||||
|
info: "SpotifService----------- connectToSpotifyRemote $result"); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError(info: "SpotifService------------ PlatformException$e"); |
||||
|
} on MissingPluginException { |
||||
|
printError(info: "SpotifService------------ MissingPluginException"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 获取Spotify的访问令牌 |
||||
|
Future<String> getAccessToken() async { |
||||
|
try { |
||||
|
var authenticationToken = await SpotifySdk.getAccessToken( |
||||
|
clientId: SPOTIFY_CLIENT_ID, |
||||
|
redirectUrl: SPOTIFY_REDIRECT_URL, |
||||
|
scope: 'app-remote-control, ' |
||||
|
'user-modify-playback-state, ' |
||||
|
'playlist-read-private, ' |
||||
|
'playlist-modify-public,user-read-currently-playing', |
||||
|
); |
||||
|
printInfo( |
||||
|
info: "SpotifService----------- getAccessToken $authenticationToken"); |
||||
|
return authenticationToken; |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: |
||||
|
"SpotifService------------getAccessToken ${e.code}: ${e.message}"); |
||||
|
return Future.error('$e.code: $e.message'); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
return Future.error('not implemented'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//获取当前播放状态 |
||||
|
Future<bool> isPaused() async { |
||||
|
try { |
||||
|
var state = await SpotifySdk.getPlayerState(); |
||||
|
if (state != null) { |
||||
|
printInfo(info: "SpotifService----------- isPaused $state"); |
||||
|
return state.isPaused; |
||||
|
} else { |
||||
|
printError(info: "SpotifService----------- isPaused null"); |
||||
|
return true; |
||||
|
} |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: |
||||
|
"SpotifService------------getPlayerState ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getPlayerState not implemented"); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
//播放 音乐 |
||||
|
Future<void> play(String url) async { |
||||
|
try { |
||||
|
await SpotifySdk.play(spotifyUri: url); |
||||
|
printInfo(info: "SpotifService----------- play $url"); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError(info: "SpotifService------------play ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//暂停 音乐 |
||||
|
Future<void> pause() async { |
||||
|
try { |
||||
|
await SpotifySdk.pause(); |
||||
|
printInfo(info: "SpotifService----------- pause"); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//停止 音乐 |
||||
|
Future<void> resume() async { |
||||
|
try { |
||||
|
await SpotifySdk.resume(); |
||||
|
printInfo(info: "SpotifService----------- resume"); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//下一首 音乐 |
||||
|
Future<void> skipNext() async { |
||||
|
try { |
||||
|
await SpotifySdk.skipNext(); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//跳过上一首 |
||||
|
Future<void> skipPrevious() async { |
||||
|
try { |
||||
|
await SpotifySdk.skipPrevious(); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//获取当前播放状态 |
||||
|
Future<void> seekTo() async { |
||||
|
try { |
||||
|
await SpotifySdk.seekTo(positionedMilliseconds: 20000); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//获取当前播放状态 |
||||
|
Future<void> seekToRelative() async { |
||||
|
try { |
||||
|
await SpotifySdk.seekToRelativePosition(relativeMilliseconds: 20000); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//获取当前播放状态 |
||||
|
Future<void> switchToLocalDevice() async { |
||||
|
try { |
||||
|
await SpotifySdk.switchToLocalDevice(); |
||||
|
} on PlatformException catch (e) { |
||||
|
printError( |
||||
|
info: "SpotifService------------pause ${e.code}: ${e.message}"); |
||||
|
} on MissingPluginException { |
||||
|
printError( |
||||
|
info: "SpotifService------------getAccessToken not implemented"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
|
||||
|
class AnimatedGradientBackground extends StatefulWidget { |
||||
|
final Widget child; |
||||
|
|
||||
|
const AnimatedGradientBackground(this.child, {super.key}); |
||||
|
|
||||
|
@override |
||||
|
State<AnimatedGradientBackground> createState() => |
||||
|
_AnimatedGradientBackgroundState(); |
||||
|
} |
||||
|
|
||||
|
class _AnimatedGradientBackgroundState extends State<AnimatedGradientBackground> |
||||
|
with SingleTickerProviderStateMixin { |
||||
|
late AnimationController _controller; |
||||
|
late Animation<Alignment?> _beginAnimation; |
||||
|
late Animation<Alignment?> _endAnimation; |
||||
|
|
||||
|
final Color color1 = const Color(0xFFfbcfdd); |
||||
|
final Color color2 = const Color(0xFFe2e9fb); |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
|
||||
|
_controller = AnimationController( |
||||
|
vsync: this, |
||||
|
duration: const Duration(seconds: 10), |
||||
|
)..repeat(reverse: true); |
||||
|
|
||||
|
// 创建起始点动画 |
||||
|
_beginAnimation = AlignmentTween( |
||||
|
begin: Alignment.topCenter, |
||||
|
end: Alignment.bottomLeft, |
||||
|
).animate(_controller); |
||||
|
|
||||
|
// 创建结束点动画 |
||||
|
_endAnimation = AlignmentTween( |
||||
|
begin: Alignment.bottomCenter, |
||||
|
end: Alignment.topRight, |
||||
|
).animate(_controller); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose() { |
||||
|
_controller.dispose(); |
||||
|
super.dispose(); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return AnimatedBuilder( |
||||
|
animation: _controller, |
||||
|
builder: (context, child) { |
||||
|
return Container( |
||||
|
decoration: BoxDecoration( |
||||
|
gradient: LinearGradient( |
||||
|
colors: [color1, color2], |
||||
|
begin: _beginAnimation.value!, |
||||
|
end: _endAnimation.value!, |
||||
|
), |
||||
|
), |
||||
|
child: child, |
||||
|
); |
||||
|
}, |
||||
|
child: widget.child, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -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 @@ |
|||||
|
#Thu Apr 24 10:52:53 CST 2025 |
||||
|
gradle.version=8.9 |
||||
@ -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 |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue