(BusKey.KEY_CAMERA_STATUS).observe(context, { status ->
+ when(status.code) {
+ CameraStatus.ERROR -> {
+ mCamera?.stopPreview()
+ }
+ CameraStatus.ERROR_PREVIEW_SIZE -> {
+ mRequest?.let { request ->
+ val oldPreviewWidth = request.previewWidth
+ val oldPreviewHeight = request.previewHeight
+ getSuitableSize(oldPreviewWidth, oldPreviewHeight).let {
+ it ?: return@observe
+ }.also {
+ Logger.i(TAG, "Automatically select the appropriate resolution (${it.width}x${it.height})")
+ updateResolution(it.width, it.height)
+ }
+ }
+ }
+ else -> { }
+ }
+ })
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "init camera client, camera = $mCamera")
+ }
+ }
+
+ override fun onPreviewData(data: ByteArray?, format: IPreviewDataCallBack.DataFormat) {
+ data?.let {
+ val width = mRequest!!.previewWidth
+ val height = mRequest!!.previewHeight
+ // avoid preview size changed
+ if (data.size != width * height * 3 /2) {
+ return
+ }
+ when(format) {
+ IPreviewDataCallBack.DataFormat.NV21 -> {
+ YUVUtils.nv21ToYuv420sp(data, width, height)
+ }
+ }
+ mVideoProcess?.putRawData(RawData(it, it.size))
+ }
+ }
+
+ /**
+ * Open camera
+ *
+ * @param cameraView camera render view, null means offscreen render
+ */
+ fun openCamera(cameraView: IAspectRatio?, isReboot: Boolean = false) {
+ if (mCtx != null && Utils.isTargetSdkOverP(mCtx) && !CameraUtils.hasCameraPermission(mCtx)) {
+ Logger.e(TAG,"open camera failed, need Manifest.permission.CAMERA permission")
+ return
+ }
+ Logger.i(TAG, "start open camera request = $mRequest, gl = $isEnableGLEs")
+ initEncodeProcessor()
+ val previewWidth = mRequest!!.previewWidth
+ val previewHeight = mRequest!!.previewHeight
+ when (cameraView) {
+ is AspectRatioSurfaceView -> {
+ if (! isEnableGLEs) {
+ cameraView.postUITask {
+ mCamera?.startPreview(mRequest!!, cameraView.holder)
+ mCamera?.addPreviewDataCallBack(this)
+ }
+ }
+ cameraView.setAspectRatio(previewWidth, previewHeight)
+ cameraView
+ }
+ is AspectRatioTextureView -> {
+ if (! isEnableGLEs) {
+ cameraView.postUITask {
+ mCamera?.startPreview(mRequest!!, cameraView.surfaceTexture)
+ mCamera?.addPreviewDataCallBack(this)
+ }
+ }
+ cameraView.setAspectRatio(previewWidth, previewHeight)
+ cameraView
+ }
+ else -> {
+ cameraView
+ }
+ }.also { view->
+ // If view is null, should cache the last set
+ // otherwise it can't recover the last status
+ mCameraView = view ?: mCameraView
+
+ // using opengl es
+ // cameraView is null, means offscreen render
+ if (! isEnableGLEs) return
+ view.apply {
+ val listener = object : RenderManager.CameraSurfaceTextureListener {
+ override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture?) {
+ surfaceTexture?.let {
+ mCamera?.startPreview(mRequest!!, it)
+ mCamera?.addPreviewDataCallBack(this@CameraClient)
+ }
+ }
+ }
+ if (this == null) {
+ Logger.i(TAG, "Offscreen render, width=$previewWidth, height=$previewHeight")
+ mRenderManager?.startRenderScreen(previewWidth, previewHeight, null, listener)
+ if (isReboot) {
+ mRenderManager?.getCacheEffectList()?.forEach { effect ->
+ mRenderManager?.addRenderEffect(effect)
+ }
+ return@apply
+ }
+ mRenderManager?.addRenderEffect(mDefaultEffect)
+ return@apply
+ }
+ postUITask {
+ val surfaceWidth = getSurfaceWidth()
+ val surfaceHeight = getSurfaceHeight()
+ val surface = getSurface()
+ mRenderManager?.startRenderScreen(surfaceWidth, surfaceHeight, surface, listener)
+ mRenderManager?.setRotateType(mDefaultRotateType)
+ if (isReboot) {
+ mRenderManager?.getCacheEffectList()?.forEach { effect ->
+ mRenderManager?.addRenderEffect(effect)
+ }
+ return@postUITask
+ }
+ mRenderManager?.addRenderEffect(mDefaultEffect)
+ Logger.i(TAG, "Display render, width=$surfaceWidth, height=$surfaceHeight")
+ }
+ }
+ }
+ }
+
+ /**
+ * Close camera
+ */
+ fun closeCamera() {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "closeCamera...")
+ }
+ releaseEncodeProcessor()
+ if (isEnableGLEs) {
+ mRenderManager?.stopRenderScreen()
+ }
+ mCamera?.stopPreview()
+ }
+
+ /**
+ * Rotate camera render angle
+ *
+ * @param type rotate angle, null means rotating nothing
+ * see [RotateType.ANGLE_90], [RotateType.ANGLE_270],...etc.
+ */
+ fun setRotateType(type: RotateType?) {
+ mRenderManager?.setRotateType(type)
+ }
+
+ /**
+ * Set render size
+ *
+ * @param width surface width
+ * @param height surface height
+ */
+ fun setRenderSize(width: Int, height: Int) {
+ mRenderManager?.setRenderSize(width, height)
+ }
+
+ /**
+ * Add render effect.There is only one setting in the same category
+ *
+ * The default effects:
+ * @see [com.jiangdg.ausbc.render.effect.EffectBlackWhite]
+ * @see [com.jiangdg.ausbc.render.effect.EffectZoom]
+ * @see [com.jiangdg.ausbc.render.effect.EffectSoul]
+ *
+ * Of course, you can also realize a custom effect by extending from [AbstractEffect]
+ *
+ * @param effect a effect
+ */
+ fun addRenderEffect(effect: AbstractEffect) {
+ mRenderManager?.addRenderEffect(effect)
+ }
+
+ /**
+ * Remove render effect
+ *
+ * @param effect a effect, extending from [AbstractEffect]
+ */
+ fun removeRenderEffect(effect: AbstractEffect) {
+ mRenderManager?.removeRenderEffect(effect)
+ }
+
+ /**
+ * Update render effect
+ *
+ * @param classifyId effect classify id
+ * @param effect new effect, null means set none
+ */
+ fun updateRenderEffect(classifyId: Int, effect: AbstractEffect?) {
+ mRenderManager?.getCacheEffectList()?.find {
+ it.getClassifyId() == classifyId
+ }?.also {
+ removeRenderEffect(it)
+ }
+ effect ?: return
+ addRenderEffect(effect)
+ }
+
+ /**
+ * Switch camera
+ *
+ * @param cameraId camera id
+ */
+ fun switchCamera(cameraId: String? = null) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "switchCamera, id = $cameraId")
+ }
+ mCamera?.switchCamera(cameraId)
+ }
+
+ /**
+ * Capture image
+ *
+ * @param callBack capture a image status, see [ICaptureCallBack]
+ * @param path image save path, default is DICM/Camera
+ */
+ fun captureImage(callBack: ICaptureCallBack, path: String? = null) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "captureImage...")
+ }
+ if (isEnableGLEs && ! rawImage) {
+ mRenderManager?.saveImage(callBack, path)
+ return
+ }
+ mCamera?.captureImage(callBack, path)
+ }
+
+ /**
+ * check if camera opened
+ *
+ * @return camera open status, true or false
+ */
+ fun isCameraOpened() = mCamera?.isCameraOpened()
+
+ /**
+ * Start play mic
+ *
+ * @param callBack play mic status in real-time, see [IPlayCallBack]
+ */
+ fun startPlayMic(callBack: IPlayCallBack?) {
+ (mAudioProcess as? AACEncodeProcessor)?.playAudioStart(callBack)
+ }
+
+ /**
+ * Stop play mic
+ */
+ fun stopPlayMic() {
+ (mAudioProcess as? AACEncodeProcessor)?.playAudioStop()
+ }
+
+ /**
+ * Start push
+ */
+ fun startPush() {
+ mVideoProcess?.startEncode()
+ mAudioProcess?.startEncode()
+ }
+
+ /**
+ * Start rec mp3
+ *
+ * @param mp3Path mp3 save path
+ * @param callBack record status, see [ICaptureCallBack]
+ */
+ fun captureAudioStart(callBack: ICaptureCallBack, mp3Path: String?=null) {
+ val path = if (mp3Path.isNullOrEmpty()) {
+ "${mCtx?.getExternalFilesDir(null)?.path}/${System.currentTimeMillis()}.mp3"
+ } else {
+ mp3Path
+ }
+ (mAudioProcess as? AACEncodeProcessor)?.recordMp3Start(path, callBack)
+ }
+
+ /**
+ * Stop rec mp3
+ */
+ fun captureAudioStop() {
+ (mAudioProcess as? AACEncodeProcessor)?.recordMp3Stop()
+ }
+
+ /**
+ * Stop push
+ */
+ fun stopPush() {
+ mVideoProcess?.stopEncode()
+ mAudioProcess?.stopEncode()
+ }
+
+ /**
+ * Add encode data call back
+ *
+ * @param callBack camera encoded data call back, see [IEncodeDataCallBack]
+ */
+ fun addEncodeDataCallBack(callBack: IEncodeDataCallBack) {
+ mVideoProcess?.addEncodeDataCallBack(callBack)
+ mAudioProcess?.addEncodeDataCallBack(callBack)
+ }
+
+ /**
+ * Add preview raw data call back
+ *
+ * @param callBack camera preview data call back, see [IPreviewDataCallBack]
+ */
+ fun addPreviewDataCallBack(callBack: IPreviewDataCallBack) {
+ mCamera?.addPreviewDataCallBack(callBack)
+ }
+
+ /**
+ * Capture video start
+ *
+ * @param callBack capture result callback, see [ICaptureCallBack]
+ * @param path video save path, default is DICM/Camera
+ * @param durationInSec video file auto divide duration is seconds
+ */
+ fun captureVideoStart(callBack: ICaptureCallBack, path: String ?= null, durationInSec: Long = 0L) {
+ mMediaMuxer = Mp4Muxer(mCtx, callBack, path, durationInSec)
+ (mVideoProcess as? H264EncodeProcessor)?.apply {
+ setEncodeRate(mEncodeBitRate, mEncodeFrameRate)
+ startEncode()
+ setMp4Muxer(mMediaMuxer!!, true)
+ setOnEncodeReadyListener(object : H264EncodeProcessor.OnEncodeReadyListener {
+ override fun onReady(surface: Surface?) {
+ if (! isEnableGLEs) {
+ return
+ }
+ if (surface == null) {
+ Logger.e(TAG, "Input surface can't be null.")
+ return
+ }
+ mRenderManager?.startRenderCodec(surface, width, height)
+ }
+ })
+ }
+ (mAudioProcess as? AACEncodeProcessor)?.apply {
+ startEncode()
+ setMp4Muxer(mMediaMuxer!!, false)
+ }
+ }
+
+ /**
+ * Capture video stop
+ */
+ fun captureVideoStop() {
+ mRenderManager?.stopRenderCodec()
+ mMediaMuxer?.release()
+ mVideoProcess?.stopEncode()
+ mAudioProcess?.stopEncode()
+ mMediaMuxer = null
+ }
+
+ /**
+ * Update resolution
+ *
+ * @param width camera preview width, see [PreviewSize]
+ * @param height camera preview height, [PreviewSize]
+ * @return result of operation
+ */
+ @MainThread
+ fun updateResolution(width: Int, height: Int): Boolean {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "updateResolution size = ${width}x${height}")
+ }
+ getCameraRequest().apply {
+ if (this == null) {
+ Logger.e(TAG, "updateResolution failed, camera request is null.")
+ return false
+ }
+ if (mVideoProcess?.isEncoding() == true) {
+ Logger.e(TAG, "updateResolution failed, video recording...")
+ return false
+ }
+ previewWidth = width
+ previewHeight = height
+ closeCamera()
+ mMainHandler.postDelayed({
+ openCamera(mCameraView, true)
+ }, 500)
+ }
+ return true
+ }
+
+
+ /**
+ * Get all preview sizes
+ *
+ * @param aspectRatio
+ * @return [PreviewSize] list of camera
+ */
+ fun getAllPreviewSizes(aspectRatio: Double? = null): MutableList? {
+ return mCamera?.getAllPreviewSizes(aspectRatio)
+ }
+
+ /**
+ * Get camera request
+ *
+ * @return a camera request, see [CameraRequest]
+ */
+ fun getCameraRequest() = mRequest
+
+ /**
+ * Get camera strategy
+ *
+ * @return camera strategy, see [ICameraStrategy]
+ */
+ fun getCameraStrategy() = mCamera
+
+ /**
+ * Get default effect
+ *
+ * @return default effect, see [AbstractEffect]
+ */
+ fun getDefaultEffect() = mDefaultEffect
+
+ /**
+ * Send camera command
+ *
+ * Only effect on uvc camera
+ *
+ * This method cannot be verified, please use it with caution
+ */
+ fun sendCameraCommand(command: Int): Int? {
+ if (mCamera !is CameraUvcStrategy) {
+ return null
+ }
+ return mCamera.sendCameraCommand(command)
+ }
+
+ private fun initEncodeProcessor() {
+ releaseEncodeProcessor()
+ val encodeWidth = if (isEnableGLEs) {
+ mRequest!!.previewHeight
+ } else {
+ mRequest!!.previewWidth
+ }
+ val encodeHeight = if (isEnableGLEs) {
+ mRequest!!.previewWidth
+ } else {
+ mRequest!!.previewHeight
+ }
+ mAudioProcess = AACEncodeProcessor()
+ mVideoProcess = H264EncodeProcessor(encodeWidth, encodeHeight, isEnableGLEs)
+ }
+
+ private fun releaseEncodeProcessor() {
+ (mAudioProcess as? AACEncodeProcessor)?.playAudioStop()
+ mVideoProcess?.stopEncode()
+ mAudioProcess?.stopEncode()
+ mVideoProcess = null
+ mAudioProcess = null
+ }
+
+ private fun addLifecycleObserver(context: Context) {
+ (context as LifecycleOwner).lifecycle.addObserver(object : LifecycleEventObserver {
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ when (event) {
+ Lifecycle.Event.ON_DESTROY -> {
+ captureVideoStop()
+ closeCamera()
+ }
+ else -> {}
+ }
+ }
+ })
+ }
+
+ private fun getSuitableSize(
+ maxWidth: Int,
+ maxHeight: Int
+ ): PreviewSize? {
+ val sizeList = getAllPreviewSizes()
+ // find it
+ sizeList?.find {
+ it.width == maxWidth && it.height == maxHeight
+ }.also { size ->
+ size ?: return@also
+ return size
+ }
+ // find the same aspectRatio
+ val aspectRatio = maxWidth.toFloat() / maxHeight
+ sizeList?.find {
+ val w = it.width
+ val h = it.height
+ val ratio = w.toFloat() / h
+ ratio == aspectRatio && w <= maxWidth && h <= maxHeight
+ }.also { size ->
+ size ?: return@also
+ return size
+ }
+ // find the closest aspectRatio
+ var minDistance: Int = maxWidth
+ var closetSize: PreviewSize? = null
+ sizeList?.forEach { size ->
+ if (minDistance >= abs((maxWidth - size.width))) {
+ minDistance = abs(maxWidth - size.width)
+ closetSize = size
+ }
+ }
+ return closetSize
+ }
+
+ companion object {
+ private const val TAG = "CameraClient"
+
+ @JvmStatic
+ fun newBuilder(ctx: Context) = Builder(ctx)
+ }
+
+ class Builder constructor() {
+ internal var context: Context? = null
+ internal var cameraRequest: CameraRequest? = null
+ internal var enableGLEs: Boolean = true
+ internal var rawImage: Boolean = true
+ internal var camera: ICameraStrategy? = null
+ internal var defaultEffect: AbstractEffect? = null
+ internal var videoEncodeBitRate: Int? = null
+ internal var videoEncodeFrameRate: Int? = null
+ internal var defaultRotateType: RotateType? = null
+
+ constructor(context: Context) : this() {
+ this.context = context
+ }
+
+ /**
+ * Set camera strategy
+ *
+ * @param camera camera strategy, see [ICameraStrategy]
+ * @return [CameraClient.Builder]
+ */
+ fun setCameraStrategy(camera: ICameraStrategy?): Builder {
+ this.camera = camera
+ return this
+ }
+
+ /**
+ * Set camera request
+ *
+ * @param request camera request, see [CameraRequest]
+ * @return [CameraClient.Builder]
+ */
+ fun setCameraRequest(request: CameraRequest): Builder {
+ this.cameraRequest = request
+ return this
+ }
+
+ /**
+ * Set enable opengl es
+ *
+ * @param enable should render by opengl es,
+ * If you want to offscreen rendering, you must set it to true!
+ * @return [CameraClient.Builder]
+ */
+ fun setEnableGLES(enable: Boolean): Builder {
+ this.enableGLEs = enable
+ return this
+ }
+
+ /**
+ * Need opengl es image when capture image
+ *
+ * @param rawImage default is true
+ * @return [CameraClient.Builder]
+ */
+ fun setRawImage(rawImage: Boolean): Builder {
+ this.rawImage = rawImage
+ return this
+ }
+
+ /**
+ * Set default effect
+ *
+ * @param effect default effect,
+ * see [com.jiangdg.ausbc.render.effect.EffectBlackWhite], [com.jiangdg.ausbc.render.effect.EffectZoom] etc.
+ * @return [CameraClient.Builder]
+ */
+ fun setDefaultEffect(effect: AbstractEffect): Builder {
+ this.defaultEffect = effect
+ return this
+ }
+
+ /**
+ * Set video encode bit rate
+ *
+ * @param bitRate bit rate for h264 encoding
+ * @return [CameraClient.Builder]
+ */
+ @Deprecated("Not realized")
+ fun setVideoEncodeBitRate(bitRate: Int): Builder {
+ this.videoEncodeBitRate = bitRate
+ return this
+ }
+
+ /**
+ * Set video encode frame rate
+ *
+ * @param frameRate frame rate for h264 encoding
+ * @return [CameraClient.Builder]
+ */
+ @Deprecated("Not realized")
+ fun setVideoEncodeFrameRate(frameRate: Int): Builder {
+ this.videoEncodeFrameRate = frameRate
+ return this
+ }
+
+ /**
+ * Open debug
+ *
+ * @param debug debug switch
+ * @return [CameraClient.Builder]
+ */
+ fun openDebug(debug: Boolean): Builder {
+ UVCCamera.DEBUG = debug
+ USBMonitor.DEBUG = debug
+ Utils.debugCamera = debug
+ return this
+ }
+
+ /**
+ * Set default camera render angle
+ *
+ * @param type rotate angle, null means rotating nothing
+ * see [RotateType.ANGLE_90], [RotateType.ANGLE_270],...etc.
+ */
+ fun setDefaultRotateType(type: RotateType?): Builder {
+ this.defaultRotateType = type
+ return this
+ }
+
+ override fun toString(): String {
+ return "Builder(context=$context, cameraType=$camera, " +
+ "cameraRequest=$cameraRequest, glEsVersion=$enableGLEs)"
+ }
+
+ /**
+ * Build for [CameraClient]
+ *
+ * @return [CameraClient.Builder]
+ */
+ fun build() = CameraClient(this)
+ }
+}
+
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/MultiCameraClient.kt b/libausbc/src/main/java/com/jiangdg/ausbc/MultiCameraClient.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2cccbce69fb8dbcccbc7af66f4543934665405cb
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/MultiCameraClient.kt
@@ -0,0 +1,961 @@
+package com.jiangdg.ausbc
+
+import android.content.ContentValues
+import android.content.Context
+import android.graphics.SurfaceTexture
+import android.hardware.usb.UsbDevice
+import android.os.*
+import android.provider.MediaStore
+import android.view.Surface
+import android.view.SurfaceView
+import android.view.TextureView
+import com.jiangdg.ausbc.callback.*
+import com.jiangdg.ausbc.camera.bean.CameraRequest
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.encode.AACEncodeProcessor
+import com.jiangdg.ausbc.encode.AbstractProcessor
+import com.jiangdg.ausbc.encode.H264EncodeProcessor
+import com.jiangdg.ausbc.encode.bean.RawData
+import com.jiangdg.ausbc.encode.muxer.Mp4Muxer
+import com.jiangdg.ausbc.utils.CameraUtils
+import com.jiangdg.ausbc.utils.CameraUtils.isFilterDevice
+import com.jiangdg.ausbc.utils.CameraUtils.isUsbCamera
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import com.jiangdg.ausbc.utils.Utils
+import com.jiangdg.natives.YUVUtils
+import com.serenegiant.usb.*
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.LinkedBlockingDeque
+import java.util.concurrent.TimeUnit
+import kotlin.math.abs
+
+/** Multi-road camera client
+ *
+ * @author Created by jiangdg on 2022/7/18
+ */
+class MultiCameraClient(ctx: Context, callback: IDeviceConnectCallBack?) {
+ private var mUsbMonitor: USBMonitor? = null
+ private val mMainHandler by lazy {
+ Handler(Looper.getMainLooper())
+ }
+
+ init {
+ mUsbMonitor = USBMonitor(ctx, object : USBMonitor.OnDeviceConnectListener {
+ /**
+ * Called by receive usb device inserted broadcast
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onAttach(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "attach device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(ctx, device)) {
+ return
+ }
+ mMainHandler.post {
+ callback?.onAttachDev(device)
+ }
+ }
+
+ /**
+ * Called by receive usb device pulled out broadcast
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onDetach(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onDetach device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(ctx, device)) {
+ return
+ }
+ mMainHandler.post {
+ callback?.onDetachDec(device)
+ }
+ }
+
+ /**
+ * Called by granted permission
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onConnect(
+ device: UsbDevice?,
+ ctrlBlock: USBMonitor.UsbControlBlock?,
+ createNew: Boolean
+ ) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onConnect device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(ctx, device)) {
+ return
+ }
+ mMainHandler.post {
+ callback?.onConnectDev(device, ctrlBlock)
+ }
+ }
+
+ /**
+ * Called by dis unauthorized permission
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onDisconnect(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onDisconnect device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(ctx, device)) {
+ return
+ }
+ mMainHandler.post {
+ callback?.onDisConnectDec(device, ctrlBlock)
+ }
+ }
+
+
+ /**
+ * Called by dis unauthorized permission or request permission exception
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onCancel(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onCancel device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(ctx, device)) {
+ return
+ }
+ mMainHandler.post {
+ callback?.onCancelDev(device)
+ }
+ }
+ })
+ }
+
+ /**
+ * Register usb insert broadcast
+ */
+ fun register() {
+ if (isMonitorRegistered()) {
+ return
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "register...")
+ }
+ mUsbMonitor?.register()
+ }
+
+ /**
+ * UnRegister usb insert broadcast
+ */
+ fun unRegister() {
+ if (!isMonitorRegistered()) {
+ return
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "unRegister...")
+ }
+ mUsbMonitor?.unregister()
+ }
+
+ /**
+ * Request usb device permission
+ *
+ * @param device see [UsbDevice]
+ * @return true ready to request permission
+ */
+ fun requestPermission(device: UsbDevice?): Boolean {
+ if (!isMonitorRegistered()) {
+ Logger.w(TAG, "Usb monitor haven't been registered.")
+ return false
+ }
+ mUsbMonitor?.requestPermission(device)
+ return true
+ }
+
+ /**
+ * Uvc camera has permission
+ *
+ * @param device see [UsbDevice]
+ * @return true permission granted
+ */
+ fun hasPermission(device: UsbDevice?) = mUsbMonitor?.hasPermission(device)
+
+ /**
+ * Get device list
+ *
+ * @param list filter regular
+ * @return filter device list
+ */
+ fun getDeviceList(list: List? = null): MutableList? {
+ list?.let {
+ addDeviceFilters(it)
+ }
+ return mUsbMonitor?.deviceList
+ }
+
+ /**
+ * Add device filters
+ *
+ * @param list filter regular
+ */
+ fun addDeviceFilters(list: List) {
+ mUsbMonitor?.addDeviceFilter(list)
+ }
+
+ /**
+ * Remove device filters
+ *
+ * @param list filter regular
+ */
+ fun removeDeviceFilters(list: List) {
+ mUsbMonitor?.removeDeviceFilter(list)
+ }
+
+ /**
+ * Destroy usb monitor engine
+ */
+ fun destroy() {
+ mUsbMonitor?.destroy()
+ }
+
+ fun openDebug(debug: Boolean) {
+ Utils.debugCamera = debug
+ USBMonitor.DEBUG = debug
+ UVCCamera.DEBUG = debug
+ }
+
+ private fun isMonitorRegistered() = mUsbMonitor?.isRegistered == true
+
+
+ /**
+ * Create a uvc camera
+ *
+ * @property device see [UsbDevice]
+ * @constructor Create camera
+ */
+ class Camera(private val ctx: Context, private val device: UsbDevice) : Handler.Callback {
+ private var mCameraStateCallback: ICameraStateCallBack? = null
+ private var mMediaMuxer: Mp4Muxer? = null
+ private var mCameraView: Any? = null
+ private var mCameraRequest: CameraRequest? = null
+ private var mPreviewSize: PreviewSize? = null
+ private var isPreviewed: Boolean = false
+ private var mCameraThread: HandlerThread? = null
+ private var mCameraHandler: Handler? = null
+ private var mUvcCamera: UVCCamera? = null
+ private var mPreviewCallback: IPreviewDataCallBack? = null
+ private var mEncodeDataCallBack: IEncodeDataCallBack? = null
+ private var mAudioProcess: AbstractProcessor? = null
+ private var mVideoProcess: AbstractProcessor? = null
+ private var mCtrlBlock: USBMonitor.UsbControlBlock? = null
+ private val mMainHandler: Handler by lazy {
+ Handler(Looper.getMainLooper())
+ }
+ private val mSaveImageExecutor: ExecutorService by lazy {
+ Executors.newSingleThreadExecutor()
+ }
+ private val mNV21DataQueue: LinkedBlockingDeque by lazy {
+ LinkedBlockingDeque(MAX_NV21_DATA)
+ }
+ private val mDateFormat by lazy {
+ SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault())
+ }
+ private val mCameraDir by lazy {
+ "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera"
+ }
+
+ private val frameCallBack = IFrameCallback { frame ->
+ frame?.apply {
+ val data = ByteArray(capacity())
+ get(data)
+ mPreviewCallback?.onPreviewData(data, IPreviewDataCallBack.DataFormat.NV21)
+ // for image
+ if (mNV21DataQueue.size >= MAX_NV21_DATA) {
+ mNV21DataQueue.removeLast()
+ }
+ mNV21DataQueue.offerFirst(data)
+ // for video
+ // avoid preview size changed
+ mPreviewSize?.apply {
+ if (data.size != width * height * 3 /2) {
+ return@IFrameCallback
+ }
+ YUVUtils.nv21ToYuv420sp(data, width, height)
+ mVideoProcess?.putRawData(RawData(data, data.size))
+ }
+ }
+ }
+
+ override fun handleMessage(msg: Message): Boolean {
+ when (msg.what) {
+ MSG_START_PREVIEW -> {
+ (msg.obj as Pair<*, *>).apply {
+ openCameraInternal(first, second as? CameraRequest ?: getDefaultCameraRequest())
+ }
+ }
+ MSG_STOP_PREVIEW -> {
+ closeCameraInternal()
+ }
+ MSG_CAPTURE_IMAGE -> {
+ (msg.obj as Pair<*, *>).apply {
+ captureImageInternal(first as? String, second as ICaptureCallBack)
+ }
+ }
+ MSG_SEND_COMMAND -> {
+ sendCameraCommandInternal(msg.obj as Int)
+ }
+ MSG_CAPTURE_VIDEO_START -> {
+ (msg.obj as Triple<*, *, *>).apply {
+ captureVideoStartInternal(first as? String, second as Long, third as ICaptureCallBack)
+ }
+ }
+ MSG_CAPTURE_VIDEO_STOP -> {
+ captureVideoStopInternal()
+ }
+ }
+ return true
+ }
+
+ private fun openCameraInternal(cameraView: T, request: CameraRequest) {
+ if (Utils.isTargetSdkOverP(ctx) && !CameraUtils.hasCameraPermission(ctx)) {
+ closeCamera()
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ "Has no CAMERA permission."
+ )
+ }
+ Logger.e(TAG ,"open camera failed, need Manifest.permission.CAMERA permission when targetSdk>=28")
+ return
+ }
+ if (mCtrlBlock == null) {
+ closeCamera()
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ "Usb control block can not be null "
+ )
+ }
+ return
+ }
+ // 1. create a UVCCamera
+ try {
+ mUvcCamera = UVCCamera().apply {
+ open(mCtrlBlock)
+ }
+ } catch (e: Exception) {
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ "open camera failed ${e.localizedMessage}"
+ )
+ }
+ Logger.e(TAG, "open camera failed.", e)
+ closeCamera()
+ }
+
+ // 2. set preview size and register preview callback
+ try {
+ val previewSize = getSuitableSize(request.previewWidth, request.previewHeight)
+ if (! isPreviewSizeSupported(previewSize)) {
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ "unsupported preview size"
+ )
+ }
+ closeCamera()
+ Logger.e(TAG, "open camera failed, preview size($previewSize) unsupported-> ${mUvcCamera?.supportedSizeList}")
+ return
+ }
+ initEncodeProcessor(previewSize.width, previewSize.height)
+ mPreviewSize = previewSize
+ mUvcCamera?.setPreviewSize(
+ previewSize.width,
+ previewSize.height,
+ MIN_FS,
+ MAX_FS,
+ UVCCamera.FRAME_FORMAT_MJPEG,
+ UVCCamera.DEFAULT_BANDWIDTH
+ )
+ } catch (e: Exception) {
+ try {
+ val previewSize = getSuitableSize(request.previewWidth, request.previewHeight)
+ if (! isPreviewSizeSupported(previewSize)) {
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ "unsupported preview size"
+ )
+ }
+ closeCamera()
+ Logger.e(TAG, "open camera failed, preview size($previewSize) unsupported-> ${mUvcCamera?.supportedSizeList}")
+ return
+ }
+ Logger.e(TAG, " setPreviewSize failed, try to use yuv format...")
+ mUvcCamera?.setPreviewSize(
+ mPreviewSize!!.width,
+ mPreviewSize!!.height,
+ MIN_FS,
+ MAX_FS,
+ UVCCamera.FRAME_FORMAT_YUYV,
+ UVCCamera.DEFAULT_BANDWIDTH
+ )
+ } catch (e: Exception) {
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(
+ this,
+ ICameraStateCallBack.State.ERROR,
+ e.localizedMessage
+ )
+ }
+ closeCamera()
+ Logger.e(TAG, " setPreviewSize failed, even using yuv format", e)
+ return
+ }
+ }
+ mUvcCamera?.setFrameCallback(frameCallBack, UVCCamera.PIXEL_FORMAT_YUV420SP)
+ // 3. start preview
+ mCameraView = cameraView ?: mCameraView
+ when(cameraView) {
+ is Surface -> {
+ mUvcCamera?.setPreviewDisplay(cameraView)
+ }
+ is SurfaceTexture -> {
+ mUvcCamera?.setPreviewTexture(cameraView)
+ }
+ is SurfaceView -> {
+ mUvcCamera?.setPreviewDisplay(cameraView.holder)
+ }
+ is TextureView -> {
+ mUvcCamera?.setPreviewTexture(cameraView.surfaceTexture)
+ }
+ else -> {
+ throw IllegalStateException("Only support Surface or SurfaceTexture or SurfaceView or TextureView or GLSurfaceView--$cameraView")
+ }
+ }
+ mUvcCamera?.autoFocus = true
+ mUvcCamera?.autoWhiteBlance = true
+ mUvcCamera?.startPreview()
+ mUvcCamera?.updateCameraParams()
+ isPreviewed = true
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(this, ICameraStateCallBack.State.OPENED)
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, " start preview, name = ${device.deviceName}, preview=$mPreviewSize")
+ }
+ }
+
+ private fun closeCameraInternal() {
+ isPreviewed = false
+ releaseEncodeProcessor()
+ mUvcCamera?.destroy()
+ mUvcCamera = null
+ mMainHandler.post {
+ mCameraStateCallback?.onCameraState(this, ICameraStateCallBack.State.CLOSED)
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, " stop preview, name = ${device.deviceName}, preview=$mPreviewSize")
+ }
+ }
+
+ private fun captureImageInternal(savePath: String?, callback: ICaptureCallBack) {
+ mSaveImageExecutor.submit {
+ if (! CameraUtils.hasStoragePermission(ctx)) {
+ mMainHandler.post { callback.onError("have no storage permission") }
+ Logger.e(TAG ,"open camera failed, have no storage permission")
+ return@submit
+ }
+ if (! isPreviewed || mPreviewSize == null) {
+ mMainHandler.post { callback.onError("camera not previewing") }
+ Logger.i(TAG, "captureImageInternal failed, camera not previewing")
+ return@submit
+ }
+ val data = mNV21DataQueue.pollFirst(CAPTURE_TIMES_OUT_SEC, TimeUnit.SECONDS)
+ if (data == null) {
+ mMainHandler.post { callback.onError("Times out") }
+ Logger.i(TAG, "captureImageInternal failed, times out.")
+ return@submit
+ }
+ mMainHandler.post { callback.onBegin() }
+ val date = mDateFormat.format(System.currentTimeMillis())
+ val title = savePath ?: "IMG_AUSBC_$date"
+ val displayName = savePath ?: "$title.jpg"
+ val path = savePath ?: "$mCameraDir/$displayName"
+ val location = Utils.getGpsLocation(ctx)
+ val width = mPreviewSize!!.width
+ val height = mPreviewSize!!.height
+ YUVUtils.yuv420spToNv21(data, width, height)
+ val ret = MediaUtils.saveYuv2Jpeg(path, data, width, height)
+ if (! ret) {
+ val file = File(path)
+ if (file.exists()) {
+ file.delete()
+ }
+ mMainHandler.post { callback.onError("save yuv to jpeg failed.") }
+ Logger.w(TAG, "save yuv to jpeg failed.")
+ return@submit
+ }
+ val values = ContentValues()
+ values.put(MediaStore.Images.ImageColumns.TITLE, title)
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
+ values.put(MediaStore.Images.ImageColumns.DATA, path)
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
+ values.put(MediaStore.Images.ImageColumns.LONGITUDE, location?.longitude)
+ values.put(MediaStore.Images.ImageColumns.LATITUDE, location?.latitude)
+ ctx.contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
+ mMainHandler.post { callback.onComplete(path) }
+ if (Utils.debugCamera) { Logger.i(TAG, "captureImageInternal save path = $path") }
+ }
+ }
+
+ private fun sendCameraCommandInternal(command: Int) {
+ mUvcCamera?.sendCommand(command)
+ }
+
+ private fun captureVideoStartInternal(path: String?, durationInSec: Long, callBack: ICaptureCallBack) {
+ if (! CameraUtils.hasStoragePermission(ctx) || ! CameraUtils.hasAudioPermission(ctx)) {
+ mMainHandler.post {
+ callBack.onError("have no storage or audio permission")
+ }
+ Logger.e(TAG ,"open camera failed, have no storage and audio permission")
+ return
+ }
+ mMediaMuxer = Mp4Muxer(ctx, callBack, path, durationInSec)
+ (mVideoProcess as? H264EncodeProcessor)?.apply {
+ startEncode()
+ setMp4Muxer(mMediaMuxer!!, true)
+ addEncodeDataCallBack(mEncodeDataCallBack)
+ }
+ (mAudioProcess as? AACEncodeProcessor)?.apply {
+ startEncode()
+ setMp4Muxer(mMediaMuxer!!, false)
+ addEncodeDataCallBack(mEncodeDataCallBack)
+ }
+ }
+
+ private fun captureVideoStopInternal() {
+ mMediaMuxer?.release()
+ mVideoProcess?.stopEncode()
+ mAudioProcess?.stopEncode()
+ mMediaMuxer = null
+ }
+
+ /**
+ * Set auto focus
+ *
+ * @param enable true enable auto focus
+ */
+ fun setAutoFocus(enable: Boolean) {
+ mUvcCamera?.autoFocus = enable
+ }
+
+ /**
+ * Set auto white balance
+ *
+ * @param autoWhiteBalance true enable auto white balance
+ */
+ fun setAutoWhiteBalance(autoWhiteBalance: Boolean) {
+ mUvcCamera?.autoWhiteBlance = autoWhiteBalance
+ }
+
+ /**
+ * Set zoom
+ *
+ * @param zoom zoom value, 0 means reset
+ */
+ fun setZoom(zoom: Int) {
+ mUvcCamera?.zoom = zoom
+ }
+
+ /**
+ * Get zoom
+ */
+ fun getZoom() = mUvcCamera?.zoom
+
+ /**
+ * Set gain
+ *
+ * @param gain gain value, 0 means reset
+ */
+ fun setGain(gain: Int) {
+ mUvcCamera?.gain = gain
+ }
+
+ /**
+ * Get gain
+ */
+ fun getGain() = mUvcCamera?.gain
+
+ /**
+ * Set gamma
+ *
+ * @param gamma gamma value, 0 means reset
+ */
+ fun setGamma(gamma: Int) {
+ mUvcCamera?.gamma = gamma
+ }
+
+ /**
+ * Get gamma
+ */
+ fun getGamma() = mUvcCamera?.gamma
+
+ /**
+ * Set brightness
+ *
+ * @param brightness brightness value, 0 means reset
+ */
+ fun setBrightness(brightness: Int) {
+ mUvcCamera?.brightness = brightness
+ }
+
+ /**
+ * Get brightness
+ */
+ fun getBrightness() = mUvcCamera?.brightness
+
+ /**
+ * Set contrast
+ *
+ * @param contrast contrast value, 0 means reset
+ */
+ fun setContrast(contrast: Int) {
+ mUvcCamera?.contrast = contrast
+ }
+
+ /**
+ * Get contrast
+ */
+ fun getContrast() = mUvcCamera?.contrast
+
+ /**
+ * Set sharpness
+ *
+ * @param sharpness sharpness value, 0 means reset
+ */
+ fun setSharpness(sharpness: Int) {
+ mUvcCamera?.sharpness = sharpness
+ }
+
+ /**
+ * Get sharpness
+ */
+ fun getSharpness() = mUvcCamera?.sharpness
+
+ /**
+ * Set saturation
+ *
+ * @param saturation saturation value, 0 means reset
+ */
+ fun setSaturation(saturation: Int) {
+ mUvcCamera?.saturation = saturation
+ }
+
+ /**
+ * Get saturation
+ */
+ fun getSaturation() = mUvcCamera?.saturation
+
+ /**
+ * Set hue
+ *
+ * @param hue hue value, 0 means reset
+ */
+ fun setHue(hue: Int) {
+ mUvcCamera?.hue = hue
+ }
+
+ /**
+ * Get hue
+ */
+ fun getHue() = mUvcCamera?.hue
+
+ /**
+ * Get real preview size
+ *
+ * @return see [PreviewSize]
+ */
+ fun getPreviewSize() = mPreviewSize
+
+ /**
+ * Set usb control block, when the uvc device was granted permission
+ *
+ * @param ctrlBlock see [USBMonitor.OnDeviceConnectListener]#onConnectedDev
+ */
+ fun setUsbControlBlock(ctrlBlock: USBMonitor.UsbControlBlock?) {
+ this.mCtrlBlock = ctrlBlock
+ }
+
+ /**
+ * Set camera state call back
+ *
+ * @param callback camera be opened or closed
+ */
+ fun setCameraStateCallBack(callback: ICameraStateCallBack) {
+ mCameraStateCallback = callback
+ }
+
+ /**
+ * Open camera
+ *
+ * @param cameraView render surface view,support Surface or SurfaceTexture
+ * or SurfaceView or TextureView or GLSurfaceView
+ * @param cameraRequest camera request
+ */
+ fun openCamera(cameraView: Any? = null, cameraRequest: CameraRequest? = null) {
+ mCameraRequest = cameraRequest ?: getDefaultCameraRequest()
+ val thread = HandlerThread("${device.deviceName}-${device.deviceId}").apply {
+ start()
+ }.also {
+ mCameraHandler = Handler(it.looper, this)
+ mCameraHandler?.obtainMessage(MSG_START_PREVIEW, Pair(cameraView, mCameraRequest))?.sendToTarget()
+ }
+ this.mCameraThread = thread
+ }
+
+ /**
+ * Close camera
+ */
+ fun closeCamera() {
+ mCameraHandler?.obtainMessage(MSG_STOP_PREVIEW)?.sendToTarget()
+ mCameraThread?.quitSafely()
+ mCameraThread = null
+ mCameraHandler = null
+ }
+
+ /**
+ * check if camera opened
+ *
+ * @return camera open status, true or false
+ */
+ fun isCameraOpened() = isPreviewed
+
+ /**
+ * Get current camera request
+ *
+ * @return see [CameraRequest], can be null
+ */
+ fun getCameraRequest() = mCameraRequest
+
+ /**
+ * Capture image
+ *
+ * @param callBack capture a image status, see [ICaptureCallBack]
+ * @param path image save path, default is DICM/Camera
+ */
+ fun captureImage(callBack: ICaptureCallBack, path: String? = null) {
+ Pair(path, callBack).apply {
+ mCameraHandler?.obtainMessage(MSG_CAPTURE_IMAGE, this)?.sendToTarget()
+ }
+ }
+
+ /**
+ * Capture video start
+ *
+ * @param callBack capture result callback, see [ICaptureCallBack]
+ * @param path video save path, default is DICM/Camera
+ * @param durationInSec video file auto divide duration is seconds
+ */
+ fun captureVideoStart(callBack: ICaptureCallBack, path: String? = null, durationInSec: Long = 0L) {
+ Triple(path, durationInSec, callBack).apply {
+ mCameraHandler?.obtainMessage(MSG_CAPTURE_VIDEO_START, this)?.sendToTarget()
+ }
+ }
+
+ /**
+ * Capture video stop
+ */
+ fun captureVideoStop() {
+ mCameraHandler?.obtainMessage(MSG_CAPTURE_VIDEO_STOP)?.sendToTarget()
+ }
+
+ /**
+ * Send camera command
+ *
+ * This method cannot be verified, please use it with caution
+ */
+ fun sendCameraCommand(command: Int) {
+ mCameraHandler?.obtainMessage(MSG_SEND_COMMAND, command)?.sendToTarget()
+ }
+
+ /**
+ * Update resolution
+ *
+ * @param width camera preview width, see [PreviewSize]
+ * @param height camera preview height, [PreviewSize]
+ * @return result of operation
+ */
+ fun updateResolution(width: Int, height: Int) {
+ if (mCameraRequest == null) {
+ Logger.w(TAG, "updateResolution failed, please open camera first.")
+ return
+ }
+ if (mVideoProcess?.isEncoding() == true) {
+ Logger.e(TAG, "updateResolution failed, video recording...")
+ return
+ }
+ closeCamera()
+ mMainHandler.postDelayed({
+ mCameraRequest!!.previewWidth = width
+ mCameraRequest!!.previewHeight = height
+ openCamera(mCameraView, mCameraRequest)
+ }, 100)
+ }
+
+ /**
+ * Is record video
+ */
+ fun isRecordVideo() = mVideoProcess?.isEncoding() == true
+
+ /**
+ * Add encode data call back
+ *
+ * @param callBack camera encoded data call back, see [IEncodeDataCallBack]
+ */
+ fun addEncodeDataCallBack(callBack: IEncodeDataCallBack) {
+ this.mEncodeDataCallBack = callBack
+ }
+
+ /**
+ * Add preview raw data call back
+ *
+ * @param callBack camera preview data call back, see [IPreviewDataCallBack]
+ */
+ fun addPreviewDataCallBack(callBack: IPreviewDataCallBack) {
+ this.mPreviewCallback = callBack
+ }
+
+ /**
+ * Get usb device information
+ *
+ * @return see [UsbDevice]
+ */
+ fun getUsbDevice() = device
+
+ /**
+ * Get all preview sizes
+ *
+ * @param aspectRatio aspect ratio
+ * @return [PreviewSize] list of camera
+ */
+ fun getAllPreviewSizes(aspectRatio: Double? = null): MutableList {
+ val previewSizeList = arrayListOf()
+ if (mUvcCamera?.supportedSizeList?.isNotEmpty() == true) {
+ mUvcCamera?.supportedSizeList
+ } else {
+ mUvcCamera?.getSupportedSizeList(UVCCamera.FRAME_FORMAT_YUYV)
+ }.also { sizeList ->
+ sizeList?.forEach { size ->
+ val width = size.width
+ val height = size.height
+ val ratio = width.toDouble() / height
+ if (aspectRatio == null || aspectRatio == ratio) {
+ previewSizeList.add(PreviewSize(width, height))
+ }
+ }
+ }
+ if (Utils.debugCamera)
+ Logger.i(TAG, "aspect ratio = $aspectRatio, getAllPreviewSizes = $previewSizeList, ")
+ return previewSizeList
+ }
+
+ private fun initEncodeProcessor(previewWidth: Int, previewHeight: Int) {
+ releaseEncodeProcessor()
+ mAudioProcess = AACEncodeProcessor()
+ mVideoProcess = H264EncodeProcessor(previewWidth, previewHeight, false)
+ }
+
+ private fun releaseEncodeProcessor() {
+ mVideoProcess?.stopEncode()
+ mAudioProcess?.stopEncode()
+ mVideoProcess = null
+ mAudioProcess = null
+ }
+
+ private fun getSuitableSize(maxWidth: Int, maxHeight: Int): PreviewSize {
+ val sizeList = getAllPreviewSizes()
+ if (sizeList.isNullOrEmpty()) {
+ return PreviewSize(DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT)
+ }
+ // find it
+ sizeList.find {
+ (it.width == maxWidth && it.height == maxHeight)
+ || (it.width == DEFAULT_PREVIEW_WIDTH || it.height == DEFAULT_PREVIEW_HEIGHT)
+ }.also { size ->
+ size ?: return@also
+ return size
+ }
+
+ // find the same aspectRatio
+ val aspectRatio = maxWidth.toFloat() / maxHeight
+ sizeList.find {
+ val w = it.width
+ val h = it.height
+ val ratio = w.toFloat() / h
+ ratio == aspectRatio && w <= maxWidth && h <= maxHeight
+ }.also { size ->
+ size ?: return@also
+ return size
+ }
+ // find the closest aspectRatio
+ var minDistance: Int = maxWidth
+ var closetSize = sizeList[0]
+ sizeList.forEach { size ->
+ if (minDistance >= abs((maxWidth - size.width))) {
+ minDistance = abs(maxWidth - size.width)
+ closetSize = size
+ }
+ }
+ return closetSize
+ }
+
+ private fun isPreviewSizeSupported(previewSize: PreviewSize): Boolean {
+ return getAllPreviewSizes().find {
+ it.width == previewSize.width && it.height == previewSize.height
+ } != null
+ }
+
+
+ private fun getDefaultCameraRequest(): CameraRequest {
+ return CameraRequest.Builder()
+ .setPreviewWidth(640)
+ .setPreviewHeight(480)
+ .create()
+ }
+ }
+
+ companion object {
+ private const val TAG = "MultiCameraClient"
+ private const val MIN_FS = 10
+ private const val MAX_FS = 60
+ private const val MSG_START_PREVIEW = 0x01
+ private const val MSG_STOP_PREVIEW = 0x02
+ private const val MSG_CAPTURE_IMAGE = 0x03
+ private const val MSG_CAPTURE_VIDEO_START = 0x04
+ private const val MSG_CAPTURE_VIDEO_STOP = 0x05
+ private const val MSG_SEND_COMMAND = 0x06
+ private const val DEFAULT_PREVIEW_WIDTH = 640
+ private const val DEFAULT_PREVIEW_HEIGHT = 480
+ private const val MAX_NV21_DATA = 5
+ private const val CAPTURE_TIMES_OUT_SEC = 3L
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseActivity.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..47a9168976c58d539ed907dabfe721f16e820421
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+
+/** Base Activity
+ * Extended from AppCompatActivity which implemented LifecycleOwner.
+ *
+ * @author Created by jiangdg on 2022/1/28
+ */
+abstract class BaseActivity: AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(getRootView(layoutInflater))
+ initView()
+ initData()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ clear()
+ }
+
+ protected abstract fun getRootView(layoutInflater: LayoutInflater): View?
+ protected open fun initView() {}
+ protected open fun initData() {}
+ protected open fun clear() {}
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseApplication.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseApplication.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3875247a973ed45112572139bf59a06010bf0b43
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseApplication.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.app.Application
+import com.jiangdg.ausbc.utils.CrashUtils
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.ToastUtils
+
+/** Base Application
+ *
+ * @author Created by jiangdg on 2022/2/28
+ */
+open class BaseApplication: Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ CrashUtils.init(this)
+ Logger.init(this)
+ ToastUtils.init(this)
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseBottomDialog.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseBottomDialog.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e8a06a66ebb054a9e21b8e2cab31c2968544b7ed
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseBottomDialog.kt
@@ -0,0 +1,97 @@
+package com.jiangdg.ausbc.base
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.jiangdg.ausbc.R
+
+/** Base bottom sheet dialog
+ *
+ * @author Created by jiangdg on 2022/7/23
+ */
+abstract class BaseBottomDialog: BottomSheetDialogFragment() {
+ private var mDismissListener: OnDismissListener? = null
+ private var mTopOffset: Int = 0
+ private var mBehavior: BottomSheetBehavior? = null
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ context ?: return super.onCreateDialog(savedInstanceState)
+ return BottomSheetDialog(requireContext(), R.style.TransparentBottomSheetStyle);
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return getRootView(inflater, container)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initView()
+ initData()
+ }
+
+ override fun onStart() {
+ super.onStart()
+ dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
+ val sheetDialog = dialog as BottomSheetDialog
+ sheetDialog.delegate.findViewById(R.id.design_bottom_sheet)?.apply {
+ background = ColorDrawable(Color.TRANSPARENT)
+ val params = layoutParams as CoordinatorLayout.LayoutParams
+ params.height = getPeekHeight()
+ layoutParams = params
+ mBehavior = BottomSheetBehavior.from(this)
+ mBehavior?.peekHeight = getPeekHeight()
+ mBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
+ }
+ }
+
+ private fun getPeekHeight(): Int {
+ (context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager)?.let { wm ->
+ val point = Point()
+ wm.defaultDisplay.getSize(point)
+ return point.y - mTopOffset
+ }
+ return WindowManager.LayoutParams.MATCH_PARENT
+ }
+
+ fun hide() {
+ mBehavior?.state = BottomSheetBehavior.STATE_HIDDEN
+ }
+
+ fun setTopOffset(offset: Int) {
+ mTopOffset = offset
+ }
+
+ fun setOnDismissListener(listener: OnDismissListener) {
+ this.mDismissListener = listener
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ mDismissListener?.onDismiss()
+ }
+
+ protected abstract fun initView()
+ protected abstract fun initData()
+ protected abstract fun getRootView(inflater: LayoutInflater, container: ViewGroup?): View?
+
+ interface OnDismissListener {
+ fun onDismiss()
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseDialog.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseDialog.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7926d7437d8226bc3aac74b12d675086ad052cdf
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseDialog.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.app.Activity
+import android.app.Dialog
+import android.content.res.Configuration
+import android.util.DisplayMetrics
+import com.jiangdg.ausbc.R
+
+abstract class BaseDialog(
+ activity: Activity,
+ portraitWidthRatio: Float = 0.67F,
+ landscapeWidthRatio: Float = 0.5F
+) : DialogInterface {
+ private val mContext: Activity = activity
+ protected val mDialog: Dialog = Dialog(mContext, R.style.CommonDialogStyle)
+
+ init {
+ mDialog.setContentView(this.getContentLayoutId())
+ val orientation = mContext.resources.configuration.orientation
+ val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE // 是否横屏
+ mDialog.window?.let {
+ // dialog的宽度 横屏设置为50% 竖屏设置为80%
+ val dm = DisplayMetrics()
+ it.windowManager?.defaultDisplay?.run {
+ getMetrics(dm)
+ val lp = it.attributes
+ lp.width = (dm.widthPixels * if (isLandscape) landscapeWidthRatio else portraitWidthRatio).toInt()
+ it.attributes = lp
+ }
+ }
+ mDialog.setCanceledOnTouchOutside(false)
+ }
+
+ protected abstract fun getContentLayoutId(): Int
+
+ final override fun getDialog(): Dialog = mDialog
+
+ override fun show() {
+ getDialog().show()
+ }
+
+ override fun dismiss() {
+ getDialog().dismiss()
+ }
+
+ override fun isShowing(): Boolean {
+ return getDialog().isShowing
+ }
+
+ override fun setCanceledOnTouchOutside(cancel: Boolean) {
+ getDialog().setCanceledOnTouchOutside(cancel)
+ }
+
+ override fun setCancelable(flag: Boolean) {
+ getDialog().setCancelable(flag)
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseFragment.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5c7003db6f9d519c3ad21b2ec89dc90396fe85a5
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/BaseFragment.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+
+/** Base fragment
+ *
+ * @author Created by jiangdg on 2022/1/21
+ */
+abstract class BaseFragment: Fragment() {
+
+ private var mRootView: View? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return getRootView(inflater, container).apply {
+ mRootView = this
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initView()
+ initData()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ clear()
+ mRootView = null
+ }
+
+ open fun isFragmentAttached(): Boolean {
+ return if (null == activity || activity!!.isDestroyed) {
+ false
+ } else isAdded && !isDetached
+ }
+
+ protected fun getRootView() = mRootView
+
+ protected abstract fun getRootView(inflater: LayoutInflater, container: ViewGroup?): View?
+ protected open fun initView() {}
+ protected open fun initData() {}
+ protected open fun clear() {}
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraActivity.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1164efe2b3046098da76429480935d477dc64d64
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraActivity.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.graphics.SurfaceTexture
+import android.view.Gravity
+import android.view.SurfaceHolder
+import android.view.TextureView
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import com.jiangdg.ausbc.CameraClient
+import com.jiangdg.ausbc.camera.Camera1Strategy
+import com.jiangdg.ausbc.camera.bean.CameraRequest
+import com.jiangdg.ausbc.render.effect.EffectBlackWhite
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.widget.AspectRatioSurfaceView
+import com.jiangdg.ausbc.widget.AspectRatioTextureView
+import com.jiangdg.ausbc.widget.IAspectRatio
+
+/** Extends from BaseActivity for CameraClient usage
+ *
+ * @author Created by jiangdg on 2022/1/28
+ */
+abstract class CameraActivity : BaseActivity(){
+ private var mCameraClient: CameraClient? = null
+
+ override fun initData() {
+ when (val cameraView = getCameraView()) {
+ is AspectRatioTextureView -> {
+ handleTextureView(cameraView)
+ cameraView
+ }
+ is AspectRatioSurfaceView -> {
+ handleSurfaceView(cameraView)
+ cameraView
+ }
+ else -> {
+ null
+ }
+ }?.let { view->
+ getCameraViewContainer()?.apply {
+ removeAllViews()
+ addView(view, getViewLayoutParams(this))
+ }
+ }
+ mCameraClient = getCameraClient() ?: getDefault()
+ }
+
+ private fun handleTextureView(textureView: AspectRatioTextureView) {
+ textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
+ override fun onSurfaceTextureAvailable(
+ surface: SurfaceTexture?,
+ width: Int,
+ height: Int
+ ) {
+ Logger.i(TAG, "handleTextureView onSurfaceTextureAvailable")
+ openCamera(textureView)
+ }
+
+ override fun onSurfaceTextureSizeChanged(
+ surface: SurfaceTexture?,
+ width: Int,
+ height: Int
+ ) {
+ Logger.i(TAG, "handleTextureView onSurfaceTextureAvailable")
+ surfaceSizeChanged(width, height)
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
+ Logger.i(TAG, "handleTextureView onSurfaceTextureDestroyed")
+ closeCamera()
+ return false
+ }
+
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
+ }
+ }
+ }
+
+ private fun handleSurfaceView(surfaceView: AspectRatioSurfaceView) {
+ surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(holder: SurfaceHolder?) {
+ Logger.i(TAG, "handleSurfaceView surfaceCreated")
+ openCamera(surfaceView)
+ }
+
+ override fun surfaceChanged(
+ holder: SurfaceHolder?,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ Logger.i(TAG, "handleSurfaceView surfaceChanged")
+ surfaceSizeChanged(width, height)
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder?) {
+ Logger.i(TAG, "handleSurfaceView surfaceDestroyed")
+ closeCamera()
+ }
+ })
+ }
+
+ private fun openCamera(st: IAspectRatio? = null) {
+ mCameraClient?.openCamera(st)
+ }
+
+ protected fun closeCamera() {
+ mCameraClient?.closeCamera()
+ }
+
+ protected fun surfaceSizeChanged(surfaceWidth: Int, surfaceHeight: Int) {
+ mCameraClient?.setRenderSize(surfaceWidth, surfaceHeight)
+ }
+
+ private fun getViewLayoutParams(viewGroup: ViewGroup): ViewGroup.LayoutParams? {
+ return when(viewGroup) {
+ is FrameLayout -> {
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ Gravity.CENTER
+ )
+ }
+ is LinearLayout -> {
+ LinearLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ ).apply {
+ gravity = Gravity.CENTER
+ }
+ }
+ is RelativeLayout -> {
+ RelativeLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ ).apply {
+ addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE)
+ addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE)
+ }
+ }
+ else -> null
+ }
+ }
+ /**
+ * Get camera view
+ *
+ * @return CameraView, such as AspectRatioTextureView etc.
+ */
+ protected abstract fun getCameraView(): IAspectRatio?
+
+ protected abstract fun getCameraViewContainer(): ViewGroup?
+
+ protected open fun getCameraClient(): CameraClient? {
+ return null
+ }
+
+ private fun getDefault(): CameraClient {
+ return CameraClient.newBuilder(this)
+ .setEnableGLES(true)
+ .setDefaultEffect(EffectBlackWhite(this))
+ .setCameraStrategy(Camera1Strategy(this))
+ .setCameraRequest(getCameraRequest())
+ .openDebug(true)
+ .build()
+ }
+
+ private fun getCameraRequest(): CameraRequest {
+ return CameraRequest.Builder()
+ .setFrontCamera(false)
+ .setContinuousAFModel(true)
+ .setContinuousAFModel(true)
+ .setPreviewWidth(1280)
+ .setPreviewHeight(720)
+ .create()
+ }
+
+ companion object {
+ private const val TAG = "CameraActivity"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraFragment.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..97e7fa757594ae51de3f276154bbf7e5a3e72771
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/CameraFragment.kt
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.graphics.SurfaceTexture
+import android.view.*
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import com.jiangdg.ausbc.CameraClient
+import com.jiangdg.ausbc.callback.ICaptureCallBack
+import com.jiangdg.ausbc.callback.IEncodeDataCallBack
+import com.jiangdg.ausbc.callback.IPlayCallBack
+import com.jiangdg.ausbc.callback.IPreviewDataCallBack
+import com.jiangdg.ausbc.camera.CameraUvcStrategy
+import com.jiangdg.ausbc.camera.ICameraStrategy
+import com.jiangdg.ausbc.camera.bean.CameraRequest
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.render.env.RotateType
+import com.jiangdg.ausbc.render.effect.AbstractEffect
+import com.jiangdg.ausbc.widget.AspectRatioSurfaceView
+import com.jiangdg.ausbc.widget.AspectRatioTextureView
+import com.jiangdg.ausbc.widget.IAspectRatio
+import java.lang.IllegalArgumentException
+
+/** Extends from BaseFragment for CameraClient usage
+ *
+ * @author Created by jiangdg on 2022/1/21
+ */
+abstract class CameraFragment : BaseFragment() {
+ private var mCameraClient: CameraClient? = null
+
+ override fun initData() {
+ mCameraClient = getCameraClient() ?: getDefault()
+ when (val cameraView = getCameraView()) {
+ is AspectRatioTextureView -> {
+ handleTextureView(cameraView)
+ cameraView
+ }
+ is AspectRatioSurfaceView -> {
+ handleSurfaceView(cameraView)
+ cameraView
+ }
+ else -> {
+ null
+ }
+ }?.let { view->
+ getCameraViewContainer()?.apply {
+ removeAllViews()
+ addView(view, getViewLayoutParams(this))
+ }
+ }
+ }
+
+ private fun handleTextureView(textureView: AspectRatioTextureView) {
+ textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
+ override fun onSurfaceTextureAvailable(
+ surface: SurfaceTexture?,
+ width: Int,
+ height: Int
+ ) {
+ openCamera(textureView)
+ }
+
+ override fun onSurfaceTextureSizeChanged(
+ surface: SurfaceTexture?,
+ width: Int,
+ height: Int
+ ) {
+ surfaceSizeChanged(width, height)
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
+ closeCamera()
+ return false
+ }
+
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
+ }
+ }
+ }
+
+ private fun handleSurfaceView(surfaceView: AspectRatioSurfaceView) {
+ surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+ override fun surfaceCreated(holder: SurfaceHolder?) {
+ openCamera(surfaceView)
+ }
+
+ override fun surfaceChanged(
+ holder: SurfaceHolder?,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ surfaceSizeChanged(width, height)
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder?) {
+ closeCamera()
+ }
+ })
+ }
+
+ /**
+ * Capture image
+ *
+ * @param callBack capture status, see [ICaptureCallBack]
+ * @param savePath custom image path
+ */
+ protected fun captureImage(callBack: ICaptureCallBack, savePath: String? = null) {
+ mCameraClient?.captureImage(callBack, savePath)
+ }
+
+ /**
+ * Switch camera
+ *
+ * @param cameraId camera id
+ */
+ protected fun switchCamera(cameraId: String? = null) {
+ mCameraClient?.switchCamera(cameraId)
+ }
+
+ /**
+ * Is camera opened
+ *
+ * @return camera open status
+ */
+ protected fun isCameraOpened() = mCameraClient?.isCameraOpened() ?: false
+
+ /**
+ * Update resolution
+ *
+ * @param width camera preview width, see [com.jiangdg.ausbc.camera.bean.PreviewSize]
+ * @param height camera preview height, see [com.jiangdg.ausbc.camera.bean.PreviewSize]
+ */
+ protected fun updateResolution(width: Int, height: Int) {
+ mCameraClient?.updateResolution(width, height)
+ }
+
+ /**
+ * Get all preview sizes
+ *
+ * @param aspectRatio preview size aspect ratio,
+ * null means getting all preview sizes
+ */
+ protected fun getAllPreviewSizes(aspectRatio: Double? = null) = mCameraClient?.getAllPreviewSizes(aspectRatio)
+
+ /**
+ * Add render effect
+ *
+ * @param effect a effect will be added, only enable opengl render worked, see [AbstractEffect]
+ */
+ protected fun addRenderEffect(effect: AbstractEffect) {
+ mCameraClient?.addRenderEffect(effect)
+ }
+
+ /**
+ * Remove render effect
+ *
+ * @param effect a effect will be removed, only enable opengl render worked, see [AbstractEffect]
+ */
+ protected fun removeRenderEffect(effect: AbstractEffect) {
+ mCameraClient?.removeRenderEffect(effect)
+ }
+
+ /**
+ * Update render effect
+ *
+ * @param classifyId effect classify id
+ * @param effect new effect, null means set none
+ */
+ protected fun updateRenderEffect(classifyId: Int, effect: AbstractEffect?) {
+ mCameraClient?.updateRenderEffect(classifyId, effect)
+ }
+
+ /**
+ * Start push
+ */
+ protected fun startPush() {
+ mCameraClient?.startPush()
+ }
+
+ /**
+ * Stop push
+ */
+ protected fun stopPush() {
+ mCameraClient?.stopPush()
+ }
+
+ /**
+ * Add encode data call back
+ *
+ * @param callBack encode data call back, see [IEncodeDataCallBack]
+ */
+ protected fun addEncodeDataCallBack(callBack: IEncodeDataCallBack) {
+ mCameraClient?.addEncodeDataCallBack(callBack)
+ }
+
+ /**
+ * Add preview data call back
+ *
+ * @param callBack preview data call back, see [IPreviewDataCallBack]
+ */
+ protected fun addPreviewDataCallBack(callBack: IPreviewDataCallBack) {
+ mCameraClient?.addPreviewDataCallBack(callBack)
+ }
+
+ /**
+ * Capture video start
+ *
+ * @param callBack capture status, see [ICaptureCallBack]
+ * @param path custom save path
+ * @param durationInSec divided record duration time in seconds
+ */
+ protected fun captureVideoStart(callBack: ICaptureCallBack, path: String ?= null, durationInSec: Long = 0L) {
+ mCameraClient?.captureVideoStart(callBack, path, durationInSec)
+ }
+
+ /**
+ * Capture video stop
+ */
+ protected fun captureVideoStop() {
+ mCameraClient?.captureVideoStop()
+ }
+
+ /**
+ * Capture audio start
+ *
+ * @param callBack capture status, see [ICaptureCallBack]
+ * @param path custom save path
+ */
+ protected fun captureAudioStart(callBack: ICaptureCallBack, path: String ?= null) {
+ mCameraClient?.captureAudioStart(callBack, path)
+ }
+
+ /**
+ * Capture audio stop
+ */
+ protected fun captureAudioStop() {
+ mCameraClient?.captureAudioStop()
+ }
+
+ /**
+ * Start play mic
+ *
+ * @param callBack play mic in real-time, see [IPlayCallBack]
+ */
+ protected fun startPlayMic(callBack: IPlayCallBack? = null) {
+ mCameraClient?.startPlayMic(callBack)
+ }
+
+ /**
+ * Stop play mic
+ */
+ protected fun stopPlayMic() {
+ mCameraClient?.stopPlayMic()
+ }
+
+ /**
+ * Get current preview size
+ *
+ * @return camera preview size, see [PreviewSize]
+ */
+ protected fun getCurrentPreviewSize(): PreviewSize? {
+ return mCameraClient?.getCameraRequest()?.let {
+ PreviewSize(it.previewWidth, it.previewHeight)
+ }
+ }
+
+ /**
+ * Get current camera strategy
+ *
+ * @return camera strategy, see [ICameraStrategy]
+ */
+ protected fun getCurrentCameraStrategy() = mCameraClient?.getCameraStrategy()
+
+ /**
+ * Get default effect
+ *
+ * @return default effect, see [AbstractEffect]
+ */
+ protected fun getDefaultEffect() = mCameraClient?.getDefaultEffect()
+
+ /**
+ * Rotate camera angle
+ *
+ * @param type rotate angle, null means rotating nothing
+ * see [RotateType.ANGLE_90], [RotateType.ANGLE_270],...etc.
+ */
+ protected fun setRotateType(type: RotateType) {
+ mCameraClient?.setRotateType(type)
+ }
+
+ /**
+ * Send camera command of uvc camera
+ *
+ * @param command hex value
+ * @return control result
+ */
+ protected fun sendCameraCommand(command: Int): Int? {
+ return mCameraClient?.sendCameraCommand(command)
+ }
+
+ protected fun openCamera(st: IAspectRatio? = null) {
+ mCameraClient?.openCamera(st)
+ }
+
+ protected fun closeCamera() {
+ mCameraClient?.closeCamera()
+ }
+
+ private fun surfaceSizeChanged(surfaceWidth: Int, surfaceHeight: Int) {
+ mCameraClient?.setRenderSize(surfaceWidth, surfaceHeight)
+ }
+
+ private fun getViewLayoutParams(viewGroup: ViewGroup): ViewGroup.LayoutParams {
+ return when(viewGroup) {
+ is FrameLayout -> {
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ getGravity()
+ )
+ }
+ is LinearLayout -> {
+ LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT
+ ).apply {
+ gravity = getGravity()
+ }
+ }
+ is RelativeLayout -> {
+ RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.MATCH_PARENT
+ ).apply{
+ when(getGravity()) {
+ Gravity.TOP -> {
+ addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE)
+ }
+ Gravity.BOTTOM -> {
+ addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE)
+ }
+ else -> {
+ addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE)
+ addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE)
+ }
+ }
+ }
+ }
+ else -> throw IllegalArgumentException("Unsupported container view, " +
+ "you can use FrameLayout or LinearLayout or RelativeLayout")
+ }
+ }
+
+ /**
+ * Get camera view
+ *
+ * @return CameraView, such as AspectRatioTextureView etc.
+ */
+ protected abstract fun getCameraView(): IAspectRatio?
+
+ /**
+ * Get camera view container
+ *
+ * @return camera view container, such as FrameLayout ect
+ */
+ protected abstract fun getCameraViewContainer(): ViewGroup?
+
+ /**
+ * Camera render view show gravity
+ */
+ protected open fun getGravity() = Gravity.CENTER
+
+ /**
+ * Get camera client
+ *
+ * @return camera client, you can custom it, see [getDefault]
+ */
+ protected open fun getCameraClient(): CameraClient? {
+ return null
+ }
+
+ private fun getDefault(): CameraClient {
+ return CameraClient.newBuilder(requireContext())
+ .setEnableGLES(true)
+ .setRawImage(true)
+ .setCameraStrategy(CameraUvcStrategy(requireContext()))
+ .setCameraRequest(getCameraRequest())
+ .setDefaultRotateType(RotateType.ANGLE_0)
+ .openDebug(true)
+ .build()
+ }
+
+ private fun getCameraRequest(): CameraRequest {
+ return CameraRequest.Builder()
+ .setFrontCamera(false)
+ .setPreviewWidth(640)
+ .setPreviewHeight(480)
+ .create()
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/DialogInterface.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/DialogInterface.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5402816c608633ba3b5921959b664f145dbbbdb9
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/DialogInterface.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.app.Dialog
+
+interface DialogInterface {
+ fun getDialog(): Dialog
+
+ fun show()
+
+ fun dismiss()
+
+ fun isShowing(): Boolean
+
+ fun setCanceledOnTouchOutside(cancel: Boolean)
+
+ fun setCancelable(flag: Boolean)
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraActivity.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a93d5f0f4fe4deccfa73b87a00477abb70c06d6a
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraActivity.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.hardware.usb.UsbDevice
+import com.jiangdg.ausbc.MultiCameraClient
+import com.jiangdg.ausbc.callback.IDeviceConnectCallBack
+import com.serenegiant.usb.USBMonitor
+
+/** Multi-road camera activity
+ *
+ * @author Created by jiangdg on 2022/7/20
+ */
+abstract class MultiCameraActivity: BaseActivity() {
+ private var mCameraClient: MultiCameraClient? = null
+ private val mCameraMap = hashMapOf()
+
+ override fun initData() {
+ mCameraClient = MultiCameraClient(this, object : IDeviceConnectCallBack {
+ override fun onAttachDev(device: UsbDevice?) {
+ device ?: return
+ MultiCameraClient.Camera(this@MultiCameraActivity, device).apply {
+ mCameraMap[device.deviceId] = this
+ onCameraAttached(this)
+ }
+ if (isAutoRequestPermission()) {
+ mCameraClient?.requestPermission(device)
+ }
+ }
+
+ override fun onDetachDec(device: UsbDevice?) {
+ mCameraMap.remove(device?.deviceId)?.apply {
+ setUsbControlBlock(null)
+ onCameraDetached(this)
+ }
+ }
+
+ override fun onConnectDev(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock?) {
+ device ?: return
+ ctrlBlock ?: return
+ mCameraMap[device.deviceId]?.apply {
+ setUsbControlBlock(ctrlBlock)
+ onCameraConnected(this)
+ }
+ }
+
+ override fun onDisConnectDec(
+ device: UsbDevice?,
+ ctrlBlock: USBMonitor.UsbControlBlock?
+ ) {
+ mCameraMap[device?.deviceId]?.apply {
+ onCameraDisConnected(this)
+ }
+ }
+
+ override fun onCancelDev(device: UsbDevice?) {
+ mCameraMap[device?.deviceId]?.apply {
+ onCameraDisConnected(this)
+ }
+ }
+ })
+ mCameraClient?.register()
+ }
+
+ override fun clear() {
+ mCameraMap.values.forEach {
+ it.closeCamera()
+ }
+ mCameraMap.clear()
+ mCameraClient?.unRegister()
+ mCameraClient?.destroy()
+ mCameraClient = null
+ }
+
+ /**
+ * On camera connected
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraConnected(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera disconnected
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraDisConnected(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera attached
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraAttached(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera detached
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraDetached(camera: MultiCameraClient.Camera)
+
+ /**
+ * Get current connected cameras
+ */
+ protected fun getCameraMap() = mCameraMap
+
+ /**
+ * Get all usb device list
+ */
+ protected fun getDeviceList() = mCameraClient?.getDeviceList()
+
+ /**
+ * Get camera client
+ */
+ protected fun getCameraClient() = mCameraClient
+
+ /**
+ * Is auto request permission
+ * default is true
+ */
+ protected fun isAutoRequestPermission() = true
+
+ /**
+ * Request permission
+ *
+ * @param device see [UsbDevice]
+ */
+ protected fun requestPermission(device: UsbDevice?) {
+ mCameraClient?.requestPermission(device)
+ }
+
+ /**
+ * Has permission
+ *
+ * @param device see [UsbDevice]
+ */
+ protected fun hasPermission(device: UsbDevice?) = mCameraClient?.hasPermission(device) == true
+
+ protected fun openDebug(debug: Boolean) {
+ mCameraClient?.openDebug(debug)
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraFragment.kt b/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e11cedd3b8583beb40e451fe8e00b22927dd9467
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/base/MultiCameraFragment.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.base
+
+import android.hardware.usb.UsbDevice
+import com.jiangdg.ausbc.MultiCameraClient
+import com.jiangdg.ausbc.callback.IDeviceConnectCallBack
+import com.serenegiant.usb.USBMonitor
+
+/** Multi-road camera fragment
+ *
+ * @author Created by jiangdg on 2022/7/20
+ */
+abstract class MultiCameraFragment: BaseFragment() {
+ private var mCameraClient: MultiCameraClient? = null
+ private val mCameraMap = hashMapOf()
+
+ override fun initData() {
+ mCameraClient = MultiCameraClient(requireContext(), object : IDeviceConnectCallBack {
+ override fun onAttachDev(device: UsbDevice?) {
+ device ?: return
+ context?.let {
+ if (mCameraMap.containsKey(device.deviceId)) {
+ return
+ }
+ MultiCameraClient.Camera(it, device).apply {
+ mCameraMap[device.deviceId] = this
+ onCameraAttached(this)
+ }
+ if (isAutoRequestPermission()) {
+ mCameraClient?.requestPermission(device)
+ }
+ }
+ }
+
+ override fun onDetachDec(device: UsbDevice?) {
+ mCameraMap.remove(device?.deviceId)?.apply {
+ setUsbControlBlock(null)
+ onCameraDetached(this)
+ }
+ }
+
+ override fun onConnectDev(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock?) {
+ device ?: return
+ ctrlBlock ?: return
+ context ?: return
+ mCameraMap[device.deviceId]?.apply {
+ setUsbControlBlock(ctrlBlock)
+ onCameraConnected(this)
+ }
+ }
+
+ override fun onDisConnectDec(
+ device: UsbDevice?,
+ ctrlBlock: USBMonitor.UsbControlBlock?
+ ) {
+ mCameraMap[device?.deviceId]?.apply {
+ onCameraDisConnected(this)
+ }
+ }
+
+ override fun onCancelDev(device: UsbDevice?) {
+ mCameraMap[device?.deviceId]?.apply {
+ onCameraDisConnected(this)
+ }
+ }
+ })
+ mCameraClient?.register()
+ }
+
+ override fun clear() {
+ mCameraMap.values.forEach {
+ it.closeCamera()
+ }
+ mCameraMap.clear()
+ mCameraClient?.unRegister()
+ mCameraClient?.destroy()
+ mCameraClient = null
+ }
+
+ /**
+ * On camera connected
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraConnected(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera disconnected
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraDisConnected(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera attached
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraAttached(camera: MultiCameraClient.Camera)
+
+ /**
+ * On camera detached
+ *
+ * @param camera see [MultiCameraClient.Camera]
+ */
+ protected abstract fun onCameraDetached(camera: MultiCameraClient.Camera)
+
+ /**
+ * Get current connected cameras
+ */
+ protected fun getCameraMap() = mCameraMap
+
+ /**
+ * Get all usb device list
+ */
+ protected fun getDeviceList() = mCameraClient?.getDeviceList()
+
+ /**
+ * Get camera client
+ */
+ protected fun getCameraClient() = mCameraClient
+
+ /**
+ * Is auto request permission
+ * default is true
+ */
+ protected fun isAutoRequestPermission() = true
+
+ /**
+ * Request permission
+ *
+ * @param device see [UsbDevice]
+ */
+ protected fun requestPermission(device: UsbDevice?) {
+ mCameraClient?.requestPermission(device)
+ }
+
+ /**
+ * Has permission
+ *
+ * @param device see [UsbDevice]
+ */
+ protected fun hasPermission(device: UsbDevice?) = mCameraClient?.hasPermission(device) == true
+
+ protected fun openDebug(debug: Boolean) {
+ mCameraClient?.openDebug(debug)
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICameraStateCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICameraStateCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bf215b7f16e3dc0ab30ec8f7a35a0604afb9a36e
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICameraStateCallBack.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.callback
+
+import com.jiangdg.ausbc.MultiCameraClient
+
+/** camera operator state
+ *
+ * @author Created by jiangdg on 2022/2/09
+ */
+interface ICameraStateCallBack {
+ fun onCameraState(self: MultiCameraClient.Camera, code: State, msg: String? = null)
+
+ enum class State {
+ OPENED, CLOSED, ERROR
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICaptureCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICaptureCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9bd95854357f8fc4e1949f7680c7b0abe9c1f3ff
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/ICaptureCallBack.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.callback
+
+/** Capture a media callback
+ *
+ * @author Created by jiangdg on 2022/1/29
+ */
+interface ICaptureCallBack {
+ fun onBegin()
+ fun onError(error: String?)
+ fun onComplete(path: String?)
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/IDeviceConnectCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IDeviceConnectCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4d23f94429df9055ca95e92aa28d8522d3402f6b
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IDeviceConnectCallBack.kt
@@ -0,0 +1,46 @@
+package com.jiangdg.ausbc.callback
+
+import android.hardware.usb.UsbDevice
+import com.serenegiant.usb.USBMonitor
+
+/**
+ * I device connect call back
+ *
+ * @author Created by jiangdg on 2022/7/19
+ */
+interface IDeviceConnectCallBack {
+ /**
+ * On attach dev
+ *
+ * @param device usb device
+ */
+ fun onAttachDev(device: UsbDevice?)
+
+ /**
+ * On detach dev
+ *
+ * @param device usb device
+ */
+ fun onDetachDec(device: UsbDevice?)
+
+ /**
+ * On connect dev
+ *
+ * @param device usb device
+ */
+ fun onConnectDev(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock? = null)
+
+ /**
+ * On dis connect dev
+ *
+ * @param device usb device
+ */
+ fun onDisConnectDec(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock? = null)
+
+ /**
+ * On cancel dev
+ *
+ * @param device usb device
+ */
+ fun onCancelDev(device: UsbDevice?)
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/IEncodeDataCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IEncodeDataCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..23a68e57128024c31f40c30ea81174e55591a594
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IEncodeDataCallBack.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.callback
+
+/** Encode data callback
+ *
+ * type = 0 video encode data -> h264
+ * type = 1 audio encode data, aac
+ *
+ * @author Created by jiangdg on 2022/1/29
+ */
+interface IEncodeDataCallBack {
+ fun onEncodeData(data: ByteArray?, size: Int, type: DataType)
+
+ enum class DataType {
+ AAC, H264
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPlayCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPlayCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2895ea034a59dff4a46fe725136bc832e24849fa
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPlayCallBack.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.callback
+
+/** Play media callback
+ *
+ * @author Created by jiangdg on 2022/2/09
+ */
+interface IPlayCallBack {
+ fun onBegin()
+ fun onError(error: String)
+ fun onComplete()
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPreviewDataCallBack.kt b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPreviewDataCallBack.kt
new file mode 100644
index 0000000000000000000000000000000000000000..849aa6a385ba66a78998eb340d759f98548fb901
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/callback/IPreviewDataCallBack.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.callback
+
+/** Camera preview data callback
+ *
+ * @author Created by jiangdg on 2022/1/29
+ */
+interface IPreviewDataCallBack {
+ fun onPreviewData(data: ByteArray?, format: DataFormat)
+
+ enum class DataFormat {
+ NV21
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera1Strategy.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera1Strategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..25967d141e67f0d0a501908404b9da39fe943e04
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera1Strategy.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera
+
+import android.app.Activity
+import android.content.ContentValues
+import android.content.Context
+import android.graphics.ImageFormat
+import android.hardware.Camera
+import android.provider.MediaStore
+import android.view.Surface
+import com.jiangdg.ausbc.callback.IPreviewDataCallBack
+import com.jiangdg.ausbc.camera.bean.CameraStatus
+import com.jiangdg.ausbc.camera.bean.CameraV1Info
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.Utils
+import java.io.File
+import kotlin.Exception
+
+/** Camera1 usage
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+@Suppress("DEPRECATION")
+class Camera1Strategy(ctx: Context) : ICameraStrategy(ctx), Camera.PreviewCallback {
+ private var mCamera: Camera? = null
+
+ override fun loadCameraInfo() {
+ val cameraInfo = Camera.CameraInfo()
+ for (cameraId in 0 until Camera.getNumberOfCameras()) {
+ Camera.getCameraInfo(cameraId, cameraInfo)
+ when(cameraInfo.facing) {
+ Camera.CameraInfo.CAMERA_FACING_FRONT -> {
+ TYPE_FRONT
+ }
+ Camera.CameraInfo.CAMERA_FACING_BACK -> {
+ TYPE_BACK
+ }
+ else -> {
+ TYPE_OTHER
+ }
+ }.also { type->
+ val info = CameraV1Info(cameraId.toString()).apply {
+ cameraType = type
+ cameraVid = cameraId + 1
+ cameraPid = cameraId + 1
+ }
+ mCameraInfoMap[type] = info
+ }
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "loadCameraInfo = $mCameraInfoMap")
+ }
+ }
+
+ override fun startPreviewInternal() {
+ createCamera()
+ setParameters()
+ realStartPreview()
+ }
+
+ override fun stopPreviewInternal() {
+ destroyCamera()
+ }
+
+ override fun captureImageInternal(savePath: String?) {
+ val jpegDataCb = Camera.PictureCallback { data, camera ->
+ mSaveImageExecutor.submit {
+ mMainHandler.post {
+ mCaptureDataCb?.onBegin()
+ }
+ val date = mDateFormat.format(System.currentTimeMillis())
+ val title = savePath ?: "IMG_JJCamera_$date"
+ val displayName = savePath ?: "$title.jpg"
+ val path = savePath ?: "$mCameraDir/$displayName"
+ val width = getRequest()?.previewWidth
+ val height = getRequest()?.previewHeight
+ val orientation = 0
+ val location = Utils.getGpsLocation(getContext())
+ // 写入文件
+ File(path).writeBytes(data)
+ // 更新
+ val values = ContentValues()
+ values.put(MediaStore.Images.ImageColumns.TITLE, title)
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
+ values.put(MediaStore.Images.ImageColumns.DATA, path)
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
+ values.put(MediaStore.Images.ImageColumns.WIDTH, width)
+ values.put(MediaStore.Images.ImageColumns.HEIGHT, height)
+ values.put(MediaStore.Images.ImageColumns.ORIENTATION, orientation)
+ values.put(MediaStore.Images.ImageColumns.LONGITUDE, location?.longitude)
+ values.put(MediaStore.Images.ImageColumns.LATITUDE, location?.latitude)
+ getContext()?.contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
+ mMainHandler.post {
+ mCaptureDataCb?.onComplete(path)
+ }
+ stopPreviewInternal()
+ startPreviewInternal()
+ realStartPreview()
+ mIsCapturing.set(false)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "takePictureInternal save path = $path")
+ }
+ }
+ }
+ if (! hasCameraPermission() || !hasStoragePermission()) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError("Have no storage or camera permission.")
+ }
+ Logger.i(TAG, "takePictureInternal failed, has no storage/camera permission.")
+ return
+ }
+ if (mIsCapturing.get()) {
+ return
+ }
+ mIsCapturing.set(true)
+ mCamera?.takePicture(null, null, null, jpegDataCb)
+ }
+
+ override fun switchCameraInternal(cameraId: String?) {
+ getRequest()?.let { request ->
+ request.isFrontCamera = !request.isFrontCamera
+ stopPreviewInternal()
+ startPreviewInternal()
+
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "switchCameraInternal")
+ }
+ }
+ }
+
+ override fun updateResolutionInternal(width: Int, height: Int) {
+ getRequest()?.let { request ->
+ request.previewWidth = width
+ request.previewHeight = height
+ stopPreviewInternal()
+ startPreviewInternal()
+ }
+ }
+
+ override fun getAllPreviewSizes(aspectRatio: Double?): MutableList? {
+ getRequest()?.let { request ->
+ val list = mutableListOf()
+ val cameraInfo = mCameraInfoMap.values.find {
+ request.cameraId == it.cameraId
+ }
+ val previewSizeList = cameraInfo?.cameraPreviewSizes ?: mutableListOf()
+ if (previewSizeList.isEmpty()) {
+ mCamera?.parameters?.supportedPreviewSizes?.forEach { size->
+ list.add(PreviewSize(size.width, size.height))
+ }
+ previewSizeList.addAll(list)
+ }
+ previewSizeList.forEach { size->
+ val width = size.width
+ val height = size.height
+ val ratio = width.toDouble() / height
+ if (aspectRatio==null || ratio == aspectRatio) {
+ list.add(size)
+ }
+ }
+ Logger.i(TAG, "getAllPreviewSizes aspect ratio = $aspectRatio, list= $list")
+ return list
+ }
+ return null
+ }
+
+ private fun createCamera() {
+ getRequest()?.let { request->
+ if (! hasCameraPermission()) {
+ Logger.i(TAG, "openCamera failed, has no camera permission.")
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "no permission"))
+ return
+ }
+ stopPreviewInternal()
+ mCamera = try {
+ if (request.isFrontCamera) {
+ val cameraId = mCameraInfoMap[TYPE_FRONT]!!.cameraId
+ Camera.open(cameraId.toInt())
+ } else {
+ Camera.open()
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "open camera failed, err = ${e.localizedMessage}", e)
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, e.localizedMessage))
+ null
+ } ?: return
+ getAllPreviewSizes()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "createCamera id = ${request.cameraId}, front camera = ${request.isFrontCamera}")
+ }
+ }
+ }
+
+ private fun setParameters() {
+ try {
+ getRequest()?.let { request ->
+ mCamera?.parameters?.apply {
+ val suitablePreviewSize = getSuitableSize(
+ supportedPreviewSizes,
+ request.previewWidth,
+ request.previewHeight
+ )
+ val width = suitablePreviewSize.width
+ val height = suitablePreviewSize.height
+ previewFormat = ImageFormat.NV21
+ pictureFormat = ImageFormat.JPEG
+ if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
+ focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
+ }
+ setPreviewSize(width, height)
+ set("orientation", "portrait")
+ set("rotation", 90)
+ request.previewWidth = width
+ request.previewHeight = height
+ }.also {
+ mCamera?.parameters = it
+ }
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "open camera failed, err = ${e.localizedMessage}", e)
+ mIsPreviewing.set(false)
+ mCamera?.setPreviewCallbackWithBuffer(null)
+ mCamera?.addCallbackBuffer(null)
+ mCamera?.release()
+ mCamera = null
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, e.localizedMessage))
+ return
+ }
+ }
+
+ private fun realStartPreview() {
+ val st = getSurfaceTexture()
+ val holder = getSurfaceHolder()
+ if (st == null && holder == null) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "surface is null"))
+ Logger.e(TAG, "realStartPreview failed, SurfaceTexture or SurfaceHolder cannot be null.")
+ return
+ }
+ try {
+ getRequest()?.let { request->
+ val width = request.previewWidth
+ val height = request.previewHeight
+ mCamera?.setDisplayOrientation(getPreviewDegree(getContext(), getRequest()?.isFrontCamera ?: false))
+ mCamera?.setPreviewCallbackWithBuffer(this)
+ mCamera?.addCallbackBuffer(ByteArray(width * height * 3 / 2))
+ if (st != null) {
+ mCamera?.setPreviewTexture(st)
+ } else {
+ mCamera?.setPreviewDisplay(holder)
+ }
+ mCamera?.startPreview()
+ mIsPreviewing.set(true)
+ postCameraStatus(CameraStatus(CameraStatus.START, Pair(width, height).toString()))
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "realStartPreview width =$width, height=$height")
+ }
+ }
+ } catch (e: Exception) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, e.localizedMessage))
+ }
+ }
+
+ private fun destroyCamera() {
+ if (! mIsPreviewing.get()) return
+ mIsPreviewing.set(false)
+ mCamera?.setPreviewCallbackWithBuffer(null)
+ mCamera?.addCallbackBuffer(null)
+ mCamera?.stopPreview()
+ mCamera?.release()
+ mCamera = null
+ postCameraStatus(CameraStatus(CameraStatus.STOP))
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "destroyCamera")
+ }
+ }
+
+ private fun getSuitableSize(
+ sizeList: MutableList,
+ maxWidth: Int,
+ maxHeight: Int
+ ): PreviewSize {
+ val aspectRatio = maxWidth.toFloat() / maxHeight
+ sizeList.forEach { size ->
+ val w = size.width
+ val h = size.height
+ val ratio = w.toFloat() / h
+ if (ratio == aspectRatio && w <= maxWidth && h <= maxHeight) {
+ return PreviewSize(w, h)
+ }
+ }
+ return if (sizeList.isEmpty()) {
+ PreviewSize(maxWidth, maxHeight)
+ } else {
+ PreviewSize(sizeList[0].width, sizeList[0].height)
+ }
+ }
+
+ private fun getPreviewDegree(context: Context?, isFrontCamera: Boolean): Int {
+ if (context !is Activity) {
+ return 90
+ }
+ val degree = when (context.windowManager.defaultDisplay.rotation) {
+ Surface.ROTATION_0 -> 0
+ Surface.ROTATION_90 -> 90
+ Surface.ROTATION_180 -> 180
+ Surface.ROTATION_270 -> 270
+ else -> 0
+ }
+ val cameraInfo = Camera.CameraInfo()
+ return if (isFrontCamera) {
+ Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, cameraInfo)
+ (360 - (cameraInfo.orientation - +degree) % 360) % 360
+ } else {
+ Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo)
+ (cameraInfo.orientation - degree + 360) % 360
+ }
+ }
+
+ override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
+ data ?: return
+ getRequest() ?: return
+ try {
+ val frameSize = getRequest()!!.previewWidth * getRequest()!!.previewHeight * 3 /2
+ if (data.size != frameSize) {
+ return
+ }
+ mPreviewDataCbList.forEach { cb ->
+ cb.onPreviewData(data, IPreviewDataCallBack.DataFormat.NV21)
+ }
+ mCamera?.addCallbackBuffer(data)
+ } catch (e: IndexOutOfBoundsException) {
+ e.printStackTrace()
+ }
+ }
+
+ companion object {
+ private const val TAG = "CameraV1"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera2Strategy.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera2Strategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c3200b05abf1a00c79d5019e6ee60f831b7a2814
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/Camera2Strategy.kt
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera
+
+import android.annotation.SuppressLint
+import android.content.ContentValues
+import android.content.Context
+import android.graphics.ImageFormat
+import android.graphics.SurfaceTexture
+import android.hardware.camera2.*
+import android.media.ImageReader
+import android.os.Build
+import android.provider.MediaStore
+import android.util.Size
+import android.view.OrientationEventListener
+import android.view.Surface
+import androidx.annotation.RequiresApi
+import com.jiangdg.ausbc.callback.IPreviewDataCallBack
+import com.jiangdg.ausbc.camera.bean.CameraStatus
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.utils.SettableFuture
+import com.jiangdg.ausbc.camera.bean.CameraV2Info
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.Utils
+import java.io.File
+import java.util.concurrent.BlockingQueue
+import java.util.concurrent.LinkedBlockingDeque
+import java.util.concurrent.TimeUnit
+import kotlin.Exception
+
+/** Camera2 usage
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+class Camera2Strategy(ctx: Context) : ICameraStrategy(ctx) {
+ private val mCaptureResults: BlockingQueue = LinkedBlockingDeque()
+ private var mImageCaptureBuilder: CaptureRequest.Builder? = null
+ private var mPreviewCaptureBuilder: CaptureRequest.Builder? = null
+ private var mCameraDeviceFuture: SettableFuture? = null
+ private var mCameraCharacteristicsFuture: SettableFuture? = null
+ private var mCameraSessionFuture: SettableFuture? = null
+ private var mImageSavePath: SettableFuture = SettableFuture()
+ private var mPreviewDataImageReader: ImageReader? = null
+ private var mJpegImageReader: ImageReader? = null
+ // 输出到屏幕的Surface
+ private var mPreviewSurface: Surface? = null
+ // 输出到预览ImageReader的Surface
+ // 便于从中获取预览数据
+ private var mPreviewDataSurface: Surface? = null
+ // 输出到拍照ImageReader的Surface
+ // 便于从中获取拍照数据
+ private var mJpegDataSurface: Surface? = null
+ private var mCameraManager: CameraManager? = null
+ private var mYUVData: ByteArray? = null
+
+ override fun loadCameraInfo() {
+ mCameraManager = getContext()?.getSystemService(Context.CAMERA_SERVICE) as? CameraManager
+ mCameraManager?.apply {
+ cameraIdList.forEach { cameraId ->
+ val characteristics = getCameraCharacteristics(cameraId)
+ when (characteristics[CameraCharacteristics.LENS_FACING]) {
+ CameraCharacteristics.LENS_FACING_FRONT -> {
+ TYPE_FRONT
+ }
+ CameraCharacteristics.LENS_FACING_BACK -> {
+ TYPE_BACK
+ }
+ else -> {
+ TYPE_OTHER
+ }
+ }.let { type ->
+ val list = mutableListOf()
+ val streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+ val sizeList = streamConfigurationMap?.getOutputSizes(SurfaceTexture::class.java)
+ sizeList?.forEach {
+ list.add(PreviewSize(it.width, it.height))
+ }
+ if (mCameraInfoMap[type] == null) {
+ val cameraInfo = CameraV2Info(cameraId).apply {
+ cameraType = type
+ cameraPreviewSizes = list
+ cameraCharacteristics = characteristics
+ cameraVid = cameraId.toInt() + 1
+ cameraPid = cameraId.toInt() + 1
+ }
+ mCameraInfoMap[type] = cameraInfo
+ }
+ }
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "loadCameraInfo success, camera = $mCameraInfoMap")
+ }
+ }
+ }
+
+ override fun startPreviewInternal() {
+ openCamera()
+ createCaptureRequestBuilders()
+ setPreviewSize()
+ setImageSize()
+ createSession()
+ realStartPreview()
+ }
+
+ override fun stopPreviewInternal() {
+ closeSession()
+ closeCamera()
+ }
+
+ override fun captureImageInternal(savePath: String?) {
+ if (! hasCameraPermission() || !hasStoragePermission()) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError("Have no storage or camera permission.")
+ }
+ Logger.i(TAG, "takePictureInternal failed, has no storage/camera permission.")
+ return
+ }
+ val cameraSession = mCameraSessionFuture?.get(3, TimeUnit.SECONDS)
+ val characteristics = mCameraCharacteristicsFuture?.get(3, TimeUnit.SECONDS)
+ val captureBuilder = mImageCaptureBuilder
+ val jpegSurface = mJpegDataSurface
+ if (cameraSession == null || characteristics==null || captureBuilder==null || jpegSurface == null) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError("camera2 init failed.")
+ }
+ Logger.e(TAG, "takePictureInternal failed, camera init error.")
+ return
+ }
+ try {
+ val captureRequest = captureBuilder.let {
+ val deviceOrientation = getDeviceOrientation()
+ val jpegOrientation = getJpegOrientation(characteristics, deviceOrientation)
+ val location = Utils.getGpsLocation(getContext())
+ captureBuilder[CaptureRequest.JPEG_ORIENTATION] = jpegOrientation
+ captureBuilder[CaptureRequest.JPEG_GPS_LOCATION] = location
+ captureBuilder[CaptureRequest.JPEG_QUALITY] = 100
+ captureBuilder.addTarget(jpegSurface)
+ captureBuilder.build()
+ }
+ mImageSavePath.set(savePath)
+ cameraSession.capture(captureRequest, mImageCaptureStateCallBack, mMainHandler)
+ } catch (e: Exception) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError(e.localizedMessage)
+ }
+ Logger.e(TAG, "takePictureInternal failed, camera access error.", e)
+ }
+
+ }
+
+ override fun switchCameraInternal(cameraId: String?) {
+ getRequest()?.let { request ->
+ request.isFrontCamera = !request.isFrontCamera
+ stopPreviewInternal()
+ startPreviewInternal()
+ }
+ }
+
+ override fun updateResolutionInternal(width: Int, height: Int) {
+ getRequest()?.let { request ->
+ request.previewWidth = width
+ request.previewHeight = height
+ stopPreviewInternal()
+ startPreviewInternal()
+ }
+ }
+
+ override fun getAllPreviewSizes(aspectRatio: Double?): MutableList {
+ val list = mutableListOf()
+ getRequest()?.let { request ->
+ val cameraInfo = mCameraInfoMap.values.find {
+ request.cameraId == it.cameraId
+ }
+ cameraInfo?.cameraPreviewSizes?.forEach { size->
+ val width = size.width
+ val height = size.height
+ val ratio = width.toDouble() / height
+ if (aspectRatio==null || ratio == aspectRatio) {
+ list.add(size)
+ }
+ }
+ }
+ Logger.i(TAG, "getAllPreviewSizes aspect ratio = $aspectRatio, list= $list")
+ return list
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun openCamera() {
+ getRequest()?.let { request->
+ mCameraDeviceFuture = SettableFuture()
+ mCameraCharacteristicsFuture = SettableFuture()
+ try {
+ if (! hasCameraPermission()) {
+ Logger.e(TAG, "openCamera failed, has no camera permission.")
+ return@let
+ }
+ if (mCameraManager == null) {
+ Logger.e(TAG, "init camera manager failed, is null!")
+ return@let
+ }
+ val cameraId = when {
+ request.isFrontCamera -> {
+ mCameraInfoMap[TYPE_FRONT]!!.cameraId
+ }
+ else -> {
+ mCameraInfoMap[TYPE_BACK]!!.cameraId
+ }
+ }
+ request.cameraId = cameraId
+ mCameraManager!!.openCamera(cameraId, mCameraStateCallBack, mMainHandler)
+ Logger.i(TAG, "openCamera success, id = $cameraId.")
+ }catch (e: CameraAccessException) {
+ closeCamera()
+ Logger.e(TAG, "openCamera failed, err = ${e.reason}.", e)
+ }
+ }
+ }
+
+ private fun createCaptureRequestBuilders() {
+ try {
+ val cameraDevice = mCameraDeviceFuture?.get(3, TimeUnit.SECONDS)
+ if (cameraDevice == null) {
+ Logger.e(TAG, "createCaptureRequestBuilders failed, camera device is null.")
+ return
+ }
+ getRequest()?.let { request ->
+ mPreviewCaptureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
+ if (request.isContinuousAFModel) {
+ mPreviewCaptureBuilder?.set(
+ CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
+ )
+ }
+ if (request.isContinuousAEModel) {
+ mPreviewCaptureBuilder?.set(
+ CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+ )
+ }
+ mImageCaptureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
+ Logger.i(TAG, "createCaptureRequestBuilders success.")
+ }
+
+ } catch (e: CameraAccessException) {
+ Logger.e(TAG, "createCaptureRequestBuilders failed, err = ${e.reason}", e)
+ }
+ }
+
+ private fun setPreviewSize() {
+ val characteristics = mCameraCharacteristicsFuture?.get(3, TimeUnit.SECONDS)
+ val previewSurface = if (getSurfaceTexture() != null) {
+ Surface(getSurfaceTexture())
+ } else {
+ getSurfaceHolder()?.surface
+ }
+ if (characteristics == null || previewSurface == null) {
+ Logger.e(TAG, "setPreviewSize failed. Camera characteristics is null.")
+ return
+ }
+ // 创建预览Preview Surface
+ // 缓存匹配的预览尺寸
+ getRequest()?.let { request->
+ val maxWidth = request.previewWidth
+ val maxHeight = request.previewHeight
+ val previewSize = getSuitableSize(characteristics, SurfaceTexture::class.java, maxWidth, maxHeight)
+ mPreviewSurface = previewSurface
+ request.previewWidth = previewSize.width
+ request.previewHeight = previewSize.height
+ mYUVData = ByteArray(request.previewWidth * request.previewHeight * 3 / 2)
+ // 创建预览ImageReader & Preview Data Surface
+ val imageFormat = ImageFormat.YUV_420_888
+ val streamConfigurationMap = characteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
+ if (streamConfigurationMap?.isOutputSupportedFor(imageFormat) == true) {
+ mPreviewDataImageReader = ImageReader.newInstance(previewSize.width, previewSize.height, imageFormat, 3)
+ mPreviewDataImageReader?.setOnImageAvailableListener(mPreviewAvailableListener, getCameraHandler())
+ mPreviewDataSurface = mPreviewDataImageReader?.surface
+ }
+ Logger.i(TAG, "setPreviewSize success, size = ${previewSize}.")
+ }
+ }
+
+ private fun setImageSize() {
+ val characteristics = mCameraCharacteristicsFuture?.get(3, TimeUnit.SECONDS)
+ val captureBuilder = mImageCaptureBuilder
+ if (characteristics == null) {
+ Logger.e(TAG, "setImageSize failed. Camera characteristics is null.")
+ return
+ }
+ getRequest()?.let { request->
+ // 创建Jpeg Surface
+ // 缓存匹配得到的尺寸
+ val maxWidth = request.previewWidth
+ val maxHeight = request.previewHeight
+ val imageSize = getSuitableSize(characteristics, ImageReader::class.java, maxWidth, maxHeight)
+ mJpegImageReader = ImageReader.newInstance(imageSize.width, imageSize.height, ImageFormat.JPEG, 5)
+ mJpegImageReader?.setOnImageAvailableListener(mJpegAvailableListener, getCameraHandler())
+ mJpegDataSurface = mJpegImageReader?.surface
+ request.previewWidth = imageSize.width
+ request.previewHeight = imageSize.height
+
+ // 设定缩略图尺寸
+ captureBuilder?.let {
+ val availableThumbnailSizes = characteristics[CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES]
+ val thumbnailSize = getSuitableSize(availableThumbnailSizes, maxWidth, maxHeight)
+ captureBuilder[CaptureRequest.JPEG_THUMBNAIL_SIZE] = thumbnailSize
+ }
+ Logger.i(TAG, "setImageSize success, size = ${imageSize}.")
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ private fun createSession() {
+ try {
+ val cameraDevice = mCameraDeviceFuture?.get(3, TimeUnit.SECONDS)
+ if (cameraDevice==null) {
+ Logger.e(TAG, "realStartPreview failed, camera init failed.")
+ stopPreviewInternal()
+ return
+ }
+ mCameraSessionFuture = SettableFuture()
+ val outputs = mutableListOf().apply {
+ mPreviewSurface?.let { add(it) }
+ mPreviewDataSurface?.let { add(it) }
+ mJpegDataSurface?.let { add(it) }
+ }
+ // 注意:这里要求回调在主线程
+ cameraDevice.createCaptureSession(outputs, mCreateSessionStateCallBack, mMainHandler)
+ Logger.i(TAG, "createSession, outputs = ${outputs.size}")
+ } catch (e: Exception) {
+ Logger.e(TAG, "createCaptureSession failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ private fun realStartPreview() {
+ val cameraDevice = mCameraDeviceFuture?.get(3, TimeUnit.SECONDS)
+ val cameraSession = mCameraSessionFuture?.get(3, TimeUnit.SECONDS)
+ if (cameraDevice==null || cameraSession == null) {
+ Logger.e(TAG, "realStartPreview failed, camera init failed.")
+ stopPreviewInternal()
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "camera init failed"))
+ return
+ }
+ val previewSurface = mPreviewSurface!!
+ val previewDataSurface = mPreviewDataSurface
+ // 防止拍照时预览丢帧
+ mImageCaptureBuilder?.let { builder ->
+ builder.addTarget(previewSurface)
+ previewDataSurface?.apply {
+ builder.addTarget(this)
+ }
+ }
+
+ // 开启预览
+ mPreviewCaptureBuilder?.let { builder ->
+ previewDataSurface?.apply {
+ builder.addTarget(this)
+ }
+ builder.addTarget(previewSurface)
+ builder.build()
+ }.also { captureRequest->
+ if (captureRequest == null) {
+ Logger.e(TAG, "realStartPreview failed, captureRequest is null.")
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "capture request is null"))
+ return
+ }
+ cameraSession.setRepeatingRequest(captureRequest, null, getCameraHandler())
+ mIsPreviewing.set(true)
+ getRequest()?.apply {
+ postCameraStatus(CameraStatus(CameraStatus.START, Pair(previewWidth, previewHeight).toString()))
+ }
+ }
+ Logger.i(TAG, "realStartPreview success!")
+ }
+
+ private fun closeSession() {
+ if (Utils.debugCamera && mIsPreviewing.get())
+ Logger.i(TAG, "closeSession success.")
+ mIsPreviewing.set(false)
+ mCameraSessionFuture?.get(10, TimeUnit.MILLISECONDS)?.close()
+ mCameraDeviceFuture?.get(10, TimeUnit.MILLISECONDS)?.close()
+ mCameraSessionFuture = null
+ mCameraDeviceFuture = null
+ }
+
+ private fun closeCamera() {
+ if (Utils.debugCamera && mIsPreviewing.get())
+ Logger.i(TAG, "closeCamera success.")
+ mPreviewDataImageReader?.close()
+ mPreviewDataImageReader = null
+ mJpegImageReader?.close()
+ mJpegImageReader = null
+ mCameraCharacteristicsFuture = null
+ postCameraStatus(CameraStatus(CameraStatus.STOP))
+ }
+
+ private fun getCameraCharacteristics(id: String): CameraCharacteristics? {
+ mCameraInfoMap.values.forEach {
+ val cameraInfo = it as CameraV2Info
+ if (cameraInfo.cameraId == id) {
+ return cameraInfo.cameraCharacteristics
+ }
+ }
+ return null
+ }
+
+ private fun getSuitableSize(
+ cameraCharacteristics: CameraCharacteristics,
+ clazz: Class<*>,
+ maxWidth: Int,
+ maxHeight: Int
+ ): Size {
+ val streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+ val supportedSizes = streamConfigurationMap?.getOutputSizes(clazz)
+ return getSuitableSize(supportedSizes, maxWidth, maxHeight)
+ }
+
+ private fun getSuitableSize(
+ sizeList: Array?,
+ maxWidth: Int,
+ maxHeight: Int
+ ): Size {
+ val aspectRatio = maxWidth.toFloat() / maxHeight
+ sizeList?.forEach { size ->
+ val w = size.width
+ val h = size.height
+ val ratio = w.toFloat() / h
+ if (ratio == aspectRatio && w <= maxWidth && h <= maxHeight) {
+ return Size(w, h)
+ }
+ }
+ return if (sizeList.isNullOrEmpty()) {
+ Size(maxWidth, maxHeight)
+ } else {
+ Size(sizeList[0].width, sizeList[0].height)
+ }
+ }
+
+ private fun getJpegOrientation(
+ characteristics: CameraCharacteristics,
+ deviceOrientation: Int
+ ): Int {
+ var myDeviceOrientation = deviceOrientation
+ if (myDeviceOrientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
+ return 0
+ }
+ val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
+ val cameraFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
+ val facingFront = cameraFacing == CameraCharacteristics.LENS_FACING_FRONT
+ myDeviceOrientation = (myDeviceOrientation + 45) / 90 * 90
+ if (facingFront) {
+ myDeviceOrientation = -myDeviceOrientation
+ }
+ return (sensorOrientation + myDeviceOrientation + 360) % 360
+ }
+
+ /**
+ * 连接相机状态监听
+ */
+ private val mCameraStateCallBack = object : CameraDevice.StateCallback() {
+ override fun onOpened(camera: CameraDevice) {
+ mCameraDeviceFuture?.set(camera)
+ mCameraCharacteristicsFuture?.set(getCameraCharacteristics(camera.id))
+ Logger.i(TAG, "connect camera success in callback.")
+ }
+
+ override fun onDisconnected(camera: CameraDevice) {
+ mCameraDeviceFuture?.set(camera)
+ stopPreviewInternal()
+ Logger.i(TAG, "disconnect camera success in callback.")
+ }
+
+ override fun onError(camera: CameraDevice, error: Int) {
+ mCameraDeviceFuture?.set(camera)
+ stopPreviewInternal()
+ Logger.i(TAG, "connect camera err = ($error) in callback.")
+ }
+ }
+
+ /**
+ * session创建状态监听
+ */
+ private val mCreateSessionStateCallBack = object : CameraCaptureSession.StateCallback() {
+ override fun onConfigured(session: CameraCaptureSession) {
+ Logger.i(TAG, "configure session success in callback!")
+ mCameraSessionFuture?.set(session)
+ }
+
+ override fun onConfigureFailed(session: CameraCaptureSession) {
+ Logger.i(TAG, "configure session failed in callback!")
+ mCameraSessionFuture?.set(session)
+ }
+ }
+
+ /**
+ * 拍照状态回调
+ */
+ private val mImageCaptureStateCallBack = object : CameraCaptureSession.CaptureCallback() {
+ override fun onCaptureStarted(
+ session: CameraCaptureSession,
+ request: CaptureRequest,
+ timestamp: Long,
+ frameNumber: Long
+ ) {
+ super.onCaptureStarted(session, request, timestamp, frameNumber)
+ mMainHandler.post {
+ mCaptureDataCb?.onBegin()
+ }
+ }
+
+ override fun onCaptureCompleted(
+ session: CameraCaptureSession,
+ request: CaptureRequest,
+ result: TotalCaptureResult
+ ) {
+ super.onCaptureCompleted(session, request, result)
+ mCaptureResults.put(result)
+ }
+ }
+
+ /**
+ * 预览数据(YUV)回调
+ * YUV_420_888[] -> NV21[YYYYYYYY VUVU]
+ *
+ */
+ private val mPreviewAvailableListener = ImageReader.OnImageAvailableListener { imageReader ->
+ val image = imageReader?.acquireNextImage()
+ image?.use {
+ val request = getRequest()
+ request ?: return@OnImageAvailableListener
+ mYUVData ?: return@OnImageAvailableListener
+ try {
+ val planes = it.planes
+ // Y通道
+ val yBuffer = planes[0].buffer
+ val yuv420pYLen = request.previewWidth * request.previewHeight
+ yBuffer.get(mYUVData!!, 0, yuv420pYLen)
+ // V通道
+ val vBuffer = planes[2].buffer
+ val vPixelStride = planes[2].pixelStride
+ for ((index, i) in (0 until vBuffer.remaining() step vPixelStride).withIndex()) {
+ mYUVData!![yuv420pYLen + 2 * index] = vBuffer.get(i)
+ }
+
+ // U通道
+ val uBuffer = planes[1].buffer
+ val uPixelStride = planes[1].pixelStride
+ for ((index, i) in (0 until uBuffer.remaining() step uPixelStride).withIndex()) {
+ mYUVData!![yuv420pYLen + (2 * index + 1)] = uBuffer.get(i)
+ }
+
+ mPreviewDataCbList.forEach { cb ->
+ cb.onPreviewData(mYUVData, IPreviewDataCallBack.DataFormat.NV21)
+ }
+ it.close()
+ } catch (e: IndexOutOfBoundsException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ /**
+ * 拍照数据(JPEG)回调
+ * JPEG数据存放在plane[0]
+ */
+ @Suppress("DEPRECATION")
+ private val mJpegAvailableListener = ImageReader.OnImageAvailableListener { imageReader ->
+ val image = imageReader?.acquireNextImage()
+ image?.use {
+ val captureResult = mCaptureResults.take()
+ val jpegBuffer = it.planes[0].buffer
+ val jpegBufferArray = ByteArray(jpegBuffer.remaining())
+ jpegBuffer.get(jpegBufferArray)
+ mSaveImageExecutor.submit {
+ var savePath: String? = null
+ try {
+ savePath = mImageSavePath.get(3, TimeUnit.SECONDS)
+ } catch (e: Exception) {
+ Logger.e(TAG, "times out.", e)
+ mMainHandler.post {
+ mCaptureDataCb?.onError("set path failed, save auto ")
+ }
+ }
+ val date = mDateFormat.format(System.currentTimeMillis())
+ val title = savePath ?: "IMG_JJCamera_$date"
+ val displayName = savePath ?: "$title.jpg"
+ val path = savePath ?: "$mCameraDir/$displayName"
+// val orientation = captureResult[CaptureResult.JPEG_ORIENTATION]
+// val location = captureResult[CaptureResult.JPEG_GPS_LOCATION]
+ // 写入文件
+ File(path).writeBytes(jpegBufferArray)
+ // 更新
+ val values = ContentValues()
+ values.put(MediaStore.Images.ImageColumns.TITLE, title)
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
+ values.put(MediaStore.Images.ImageColumns.DATA, path)
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
+// values.put(MediaStore.Images.ImageColumns.ORIENTATION, orientation)
+// values.put(MediaStore.Images.ImageColumns.LONGITUDE, location?.longitude)
+// values.put(MediaStore.Images.ImageColumns.LATITUDE, location?.latitude)
+ getContext()?.contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
+ mMainHandler.post {
+ mCaptureDataCb?.onComplete(path)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "CameraV2"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraUvcStrategy.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraUvcStrategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0772535aa8758bc607eba4fd891a5488a5d96a07
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraUvcStrategy.kt
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera
+
+import android.content.ContentValues
+import android.content.Context
+import android.hardware.usb.UsbDevice
+import android.os.Build
+import android.provider.MediaStore
+import com.jiangdg.ausbc.utils.SettableFuture
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.callback.IDeviceConnectCallBack
+import com.jiangdg.ausbc.callback.IPreviewDataCallBack
+import com.jiangdg.ausbc.camera.bean.CameraStatus
+import com.jiangdg.ausbc.camera.bean.CameraUvcInfo
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.utils.CameraUtils.isFilterDevice
+import com.jiangdg.ausbc.utils.CameraUtils.isUsbCamera
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import com.jiangdg.ausbc.utils.Utils
+import com.jiangdg.natives.YUVUtils
+import com.serenegiant.usb.DeviceFilter
+import com.serenegiant.usb.IFrameCallback
+import com.serenegiant.usb.USBMonitor
+import com.serenegiant.usb.UVCCamera
+import java.io.File
+import java.util.concurrent.LinkedBlockingDeque
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.Exception
+
+/** UVC Camera usage
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+class CameraUvcStrategy(ctx: Context) : ICameraStrategy(ctx) {
+ private var mDevSettableFuture: SettableFuture? = null
+ private var mCtrlBlockSettableFuture: SettableFuture? = null
+ private val mNV21DataQueue: LinkedBlockingDeque by lazy {
+ LinkedBlockingDeque(MAX_NV21_DATA)
+ }
+ private val mRequestPermission: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+ private var mUsbMonitor: USBMonitor? = null
+ private var mUVCCamera: UVCCamera? = null
+ private var mDevConnectCallBack: IDeviceConnectCallBack? = null
+ private var mCacheDeviceList: MutableList = arrayListOf()
+
+ init {
+ register()
+ }
+
+ override fun loadCameraInfo() {
+ try {
+ val devList = getUsbDeviceListInternal()
+ if (devList.isNullOrEmpty()) {
+ val emptyTip = "Find no uvc devices, " +
+ "if you want some special device please use getUsbDeviceList() " +
+ "or add device info into default_device_filter.xml"
+ postCameraStatus(
+ CameraStatus(
+ CameraStatus.ERROR,
+ emptyTip
+ )
+ )
+ Logger.e(TAG, emptyTip)
+ return
+ }
+ devList.forEach { dev ->
+ loadCameraInfoInternal(dev)
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, " Find no uvc devices, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ private fun loadCameraInfoInternal(dev: UsbDevice) {
+ if (mCameraInfoMap.containsKey(dev.deviceId)) {
+ return
+ }
+ val cameraInfo = CameraUvcInfo(dev.deviceId.toString()).apply {
+ cameraVid = dev.vendorId
+ cameraPid = dev.productId
+ cameraName = dev.deviceName
+ cameraProtocol = dev.deviceProtocol
+ cameraClass = dev.deviceClass
+ cameraSubClass = dev.deviceSubclass
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ cameraProductName = dev.productName
+ cameraManufacturerName = dev.manufacturerName
+ }
+ }
+ mCameraInfoMap[dev.deviceId] = cameraInfo
+ }
+
+ override fun startPreviewInternal() {
+ try {
+ createCamera()
+ realStartPreview()
+ } catch (e: Exception) {
+ stopPreview()
+ Logger.e(TAG, " preview failed, err = ${e.localizedMessage}", e)
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, e.localizedMessage))
+ }
+ }
+
+ private fun createCamera(): Boolean? {
+ val ctrlBlock = mCtrlBlockSettableFuture?.get()
+ val device = mDevSettableFuture?.get()
+ device ?: return null
+ ctrlBlock ?: return null
+ getRequest()?.let { request ->
+ val previewWidth = request.previewWidth
+ val previewHeight = request.previewHeight
+ request.cameraId = device.deviceId.toString()
+ mUVCCamera = UVCCamera().apply {
+ open(ctrlBlock)
+ }
+ if (! isPreviewSizeSupported(previewWidth, previewHeight)) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR_PREVIEW_SIZE, "unsupported preview size(${request.previewWidth}, ${request.previewHeight})"))
+ Logger.e(TAG, " unsupported preview size(${request.previewWidth}, ${request.previewHeight})")
+ return null
+ }
+ try {
+ mUVCCamera?.setPreviewSize(
+ request.previewWidth,
+ request.previewHeight,
+ MIN_FS,
+ MAX_FS,
+ UVCCamera.FRAME_FORMAT_MJPEG,
+ UVCCamera.DEFAULT_BANDWIDTH
+ )
+ } catch (e: Exception) {
+ try {
+ Logger.w(TAG, " setPreviewSize failed ${e.localizedMessage}, try yuv format...")
+ if (! isPreviewSizeSupported(previewWidth, previewHeight)) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR_PREVIEW_SIZE, "unsupported preview size(${request.previewWidth}, ${request.previewHeight})"))
+ Logger.e(TAG, " unsupported preview size(${request.previewWidth}, ${request.previewHeight})")
+ return null
+ }
+ mUVCCamera?.setPreviewSize(
+ request.previewWidth,
+ request.previewHeight,
+ MIN_FS,
+ MAX_FS,
+ UVCCamera.FRAME_FORMAT_YUYV,
+ UVCCamera.DEFAULT_BANDWIDTH
+ )
+ } catch (e: Exception) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "setPreviewSize failed, err = ${e.localizedMessage}"))
+ Logger.e(TAG, " setPreviewSize failed", e)
+ return null
+ }
+ }
+ mUVCCamera?.setFrameCallback(frameCallBack, UVCCamera.PIXEL_FORMAT_YUV420SP)
+ Logger.i(TAG, " createCamera success! request = $request")
+ }
+ return true
+ }
+
+ private fun isPreviewSizeSupported(previewWidth: Int, previewHeight: Int): Boolean {
+ return getAllPreviewSizes()?.find {
+ it.width == previewWidth && it.height == previewHeight
+ } != null
+ }
+
+ private fun realStartPreview(): Boolean? {
+ try {
+ val st = getSurfaceTexture()
+ val holder = getSurfaceHolder()
+ if (st == null && holder == null) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, "surface is null"))
+ Logger.e(TAG, " SurfaceTexture or SurfaceHolder cannot be null.")
+ return null
+ }
+ if (st != null) {
+ mUVCCamera?.setPreviewTexture(st)
+ } else {
+ mUVCCamera?.setPreviewDisplay(holder)
+ }
+ mUVCCamera?.autoFocus = true
+ mUVCCamera?.autoWhiteBlance = true
+ mUVCCamera?.startPreview()
+ mUVCCamera?.updateCameraParams()
+ mIsPreviewing.set(true)
+ getRequest()?.apply {
+ postCameraStatus(
+ CameraStatus(
+ CameraStatus.START,
+ Pair(previewWidth, previewHeight).toString()
+ )
+ )
+ }
+ val dev = mDevSettableFuture?.get().apply {
+ mDevConnectCallBack?.onConnectDev(this)
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, " start preview success!!!, id(${dev?.deviceId})$dev")
+ }
+ } catch (e: Exception) {
+ postCameraStatus(CameraStatus(CameraStatus.ERROR, e.localizedMessage))
+ Logger.e(TAG, " startPreview failed. err = ${e.localizedMessage}", e)
+ return null
+ }
+ return true
+ }
+
+ override fun stopPreviewInternal() {
+ if (Utils.debugCamera && mIsPreviewing.get()) {
+ Logger.i(TAG, "stopPreviewInternal")
+ }
+ mRequestPermission.set(false)
+ mIsPreviewing.set(false)
+ mUVCCamera?.stopPreview()
+ mUVCCamera?.destroy()
+ mUVCCamera = null
+ postCameraStatus(CameraStatus(CameraStatus.STOP))
+ }
+
+ override fun captureImageInternal(savePath: String?) {
+ if (!hasCameraPermission() || !hasStoragePermission()) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError("Have no storage or camera permission.")
+ }
+ Logger.i(TAG, "captureImageInternal failed, has no storage/camera permission.")
+ return
+ }
+ if (mIsCapturing.get()) {
+ return
+ }
+ mSaveImageExecutor.submit {
+ val data = mNV21DataQueue.pollFirst(CAPTURE_TIMES_OUT_SEC, TimeUnit.SECONDS)
+ if (data == null || getRequest() == null) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError("Times out or camera request is null")
+ }
+ Logger.i(TAG, "captureImageInternal failed, times out.")
+ return@submit
+ }
+ mIsCapturing.set(true)
+ mMainHandler.post {
+ mCaptureDataCb?.onBegin()
+ }
+ val date = mDateFormat.format(System.currentTimeMillis())
+ val title = savePath ?: "IMG_JJCamera_$date"
+ val displayName = savePath ?: "$title.jpg"
+ val path = savePath ?: "$mCameraDir/$displayName"
+ val orientation = 0
+ val location = Utils.getGpsLocation(getContext())
+ val width = getRequest()!!.previewWidth
+ val height = getRequest()!!.previewHeight
+ YUVUtils.yuv420spToNv21(data, width, height)
+ val ret = MediaUtils.saveYuv2Jpeg(path, data, width, height)
+ if (!ret) {
+ val file = File(path)
+ if (file.exists()) {
+ file.delete()
+ }
+ mMainHandler.post {
+ mCaptureDataCb?.onError("save yuv to jpeg failed.")
+ }
+ Logger.w(TAG, "save yuv to jpeg failed.")
+ return@submit
+ }
+ val values = ContentValues()
+ values.put(MediaStore.Images.ImageColumns.TITLE, title)
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
+ values.put(MediaStore.Images.ImageColumns.DATA, path)
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
+ values.put(MediaStore.Images.ImageColumns.ORIENTATION, orientation)
+ values.put(MediaStore.Images.ImageColumns.LONGITUDE, location?.longitude)
+ values.put(MediaStore.Images.ImageColumns.LATITUDE, location?.latitude)
+ getContext()?.contentResolver?.insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ values
+ )
+ mMainHandler.post {
+ mCaptureDataCb?.onComplete(path)
+ }
+ mIsCapturing.set(false)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "captureImageInternal save path = $path")
+ }
+ }
+ }
+
+ override fun switchCameraInternal(cameraId: String?) {
+ getRequest()?.let {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "switchCameraInternal, camera id = $cameraId")
+ }
+ if (cameraId.isNullOrEmpty()) {
+ Logger.e(TAG, "camera id invalid.")
+ return@let
+ }
+ if (getCurrentDevice()?.deviceId?.toString() == cameraId) {
+ Logger.e(TAG, "camera was already opened.")
+ return@let
+ }
+ getUsbDeviceList()?.find {
+ cameraId == it.deviceId.toString()
+ }.also { dev ->
+ if (dev == null) {
+ Logger.e(TAG, "switch camera(: $cameraId) failed, not found.")
+ return@also
+ }
+ if (!mCacheDeviceList.contains(dev)) {
+ mCacheDeviceList.add(dev)
+ }
+ stopPreviewInternal()
+ requestCameraPermission(dev)
+ }
+ }
+ }
+
+ override fun updateResolutionInternal(width: Int, height: Int) {
+ getRequest()?.let { request ->
+ request.previewWidth = width
+ request.previewHeight = height
+ stopPreviewInternal()
+ startPreviewInternal()
+ }
+ }
+
+ override fun getAllPreviewSizes(aspectRatio: Double?): MutableList? {
+ getRequest()?.let { request ->
+ val cameraInfo = mCameraInfoMap.values.find {
+ request.cameraId == it.cameraId
+ }
+ val previewSizeList = cameraInfo?.cameraPreviewSizes ?: mutableListOf()
+ if (previewSizeList.isEmpty()) {
+ Logger.i(TAG, "getAllPreviewSizes = ${mUVCCamera?.supportedSizeList}")
+ if (mUVCCamera?.supportedSizeList?.isNotEmpty() == true) {
+ mUVCCamera?.supportedSizeList
+ } else {
+ mUVCCamera?.getSupportedSizeList(UVCCamera.FRAME_FORMAT_YUYV)
+ }.also { sizeList ->
+ sizeList?.forEach { size ->
+ previewSizeList.find {
+ it.width == size.width && it.height == size.height
+ }.also {
+ if (it == null) {
+ previewSizeList.add(PreviewSize(size.width, size.height))
+ }
+ }
+ }
+ cameraInfo?.cameraPreviewSizes = previewSizeList
+ }
+ }
+ aspectRatio ?: return previewSizeList
+ // aspect ratio list or all
+ val aspectList = mutableListOf()
+ aspectList.clear()
+ cameraInfo?.cameraPreviewSizes?.forEach { size ->
+ val width = size.width
+ val height = size.height
+ val ratio = width.toDouble() / height
+ if (ratio == aspectRatio) {
+ aspectList.add(size)
+ }
+ }
+ Logger.i(TAG, "getAllPreviewSizes aspectRatio = $aspectRatio, size = $aspectList")
+ return aspectList
+ }
+ return null
+ }
+
+ override fun register() {
+ if (mUsbMonitor?.isRegistered == true) {
+ return
+ }
+ mUsbMonitor = USBMonitor(getContext(), object : USBMonitor.OnDeviceConnectListener {
+ /**
+ * Called by receive usb device inserted broadcast
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onAttach(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "attach device = ${device?.toString()}")
+ }
+ device ?: return
+ if (!isUsbCamera(device) && !isFilterDevice(getContext(), device)) {
+ return
+ }
+ if (!mCacheDeviceList.contains(device)) {
+ device.let {
+ mCacheDeviceList.add(it)
+ }
+ mDevConnectCallBack?.onAttachDev(device)
+ }
+ loadCameraInfoInternal(device)
+ requestCameraPermission(device)
+ }
+
+ /**
+ * Called by receive usb device pulled out broadcast
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onDetach(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onDetach device = ${device?.toString()}")
+ }
+ if (!isUsbCamera(device) && !isFilterDevice(getContext(), device) && !mCacheDeviceList.contains(device)) {
+ return
+ }
+ mCameraInfoMap.remove(device?.deviceId)
+ mDevConnectCallBack?.onDetachDec(device)
+ if (mCacheDeviceList.contains(device)) {
+ mCacheDeviceList.remove(device)
+ }
+ // 重置正在打开的设备
+ val dev = mDevSettableFuture?.get()
+ if (dev?.deviceId == device?.deviceId) {
+ mRequestPermission.set(false)
+ }
+ }
+
+ /**
+ * Called by granted permission
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onConnect(
+ device: UsbDevice?,
+ ctrlBlock: USBMonitor.UsbControlBlock?,
+ createNew: Boolean
+ ) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onConnect device = ${device?.toString()}")
+ }
+ if (!isUsbCamera(device) && !isFilterDevice(getContext(), device) && !mCacheDeviceList.contains(device)) {
+ return
+ }
+ mDevSettableFuture = SettableFuture()
+ mCtrlBlockSettableFuture = SettableFuture()
+ getRequest()?.apply {
+ if (getSurfaceTexture() != null) {
+ startPreview(this, getSurfaceTexture())
+ } else {
+ startPreview(this, getSurfaceHolder())
+ }
+ }
+ mDevSettableFuture?.set(device)
+ mCtrlBlockSettableFuture?.set(ctrlBlock)
+ }
+
+ /**
+ * Called by dis unauthorized permission
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onDisconnect(device: UsbDevice?, ctrlBlock: USBMonitor.UsbControlBlock?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onDisconnect device = ${device?.toString()}")
+ }
+ if (!isUsbCamera(device) && !isFilterDevice(getContext(), device) && !mCacheDeviceList.contains(device)) {
+ return
+ }
+ val curDevice = mDevSettableFuture?.get()
+ if (curDevice?.deviceId != device?.deviceId) {
+ return
+ }
+ stopPreview()
+ mDevConnectCallBack?.onDisConnectDec(device, ctrlBlock)
+ }
+
+ /**
+ * Called by dis unauthorized permission or request permission exception
+ *
+ * @param device usb device info,see [UsbDevice]
+ */
+ override fun onCancel(device: UsbDevice?) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "onCancel device = ${device?.toString()}")
+ }
+ if (!isUsbCamera(device) && !isFilterDevice(getContext(), device) && !mCacheDeviceList.contains(device)) {
+ return
+ }
+ val curDevice = mDevSettableFuture?.get()
+ if (curDevice?.deviceId != device?.deviceId) {
+ return
+ }
+ stopPreview()
+ mDevConnectCallBack?.onDisConnectDec(device)
+ }
+ })
+ mUsbMonitor?.register()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "register uvc device monitor")
+ }
+ }
+
+ override fun unRegister() {
+ if (mUsbMonitor?.isRegistered == false) {
+ return
+ }
+ mUsbMonitor?.unregister()
+ mUsbMonitor?.destroy()
+ mUsbMonitor = null
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "unRegister uvc device monitor")
+ }
+ }
+
+ /**
+ * set device connect status call back
+ *
+ * @param cb see [IDeviceConnectCallBack]]
+ */
+ fun setDeviceConnectStatusListener(cb: IDeviceConnectCallBack) {
+ this.mDevConnectCallBack = cb
+ }
+
+ /**
+ * Get usb device list
+ *
+ * @param resId device filter regular, like [R.xml.default_device_filter]
+ * null means all usb devices, more than uvc devices
+ * @return device list
+ */
+ fun getUsbDeviceList(resId: Int? = null): MutableList? {
+ return mUsbMonitor?.deviceList?.let { usbDevList ->
+ val list = arrayListOf()
+ if (resId == null) {
+ null
+ } else {
+ DeviceFilter.getDeviceFilters(getContext(), resId)
+ }.also { filterList ->
+ if (filterList == null) {
+ list.addAll(usbDevList)
+ return@also
+ }
+ usbDevList.forEach { dev ->
+ val filterDev = filterList.find {
+ it.mProductId == dev?.productId && it.mVendorId == dev.vendorId
+ }
+ if (filterDev != null) {
+ list.add(dev)
+ }
+ }
+ }
+ list
+ }
+ }
+
+ /**
+ * Get current device in 1 seconds
+ *
+ * @return current opened [UsbDevice]
+ */
+ fun getCurrentDevice(): UsbDevice? {
+ return try {
+ mDevSettableFuture?.get(1, TimeUnit.SECONDS)
+ } catch (e: Exception) {
+ Logger.w(TAG, "get current usb device times out")
+ null
+ }
+ }
+
+ /**
+ * Send camera command
+ *
+ * @param command hex value
+ * @return control result
+ */
+ fun sendCameraCommand(command: Int): Int? {
+ return mUVCCamera?.sendCommand(command).apply {
+ Logger.i(TAG, "send command ret = $this")
+ }
+ }
+
+ /**
+ * Set auto focus
+ *
+ * @param enable true enable auto focus
+ */
+ fun setAutoFocus(enable: Boolean) {
+ mUVCCamera?.autoFocus = enable
+ }
+
+ /**
+ * Set auto white balance
+ *
+ * @param autoWhiteBalance true enable auto white balance
+ */
+ fun setAutoWhiteBalance(autoWhiteBalance: Boolean) {
+ mUVCCamera?.autoWhiteBlance = autoWhiteBalance
+ }
+
+ /**
+ * Set zoom
+ *
+ * @param zoom zoom value, 0 means reset
+ */
+ fun setZoom(zoom: Int) {
+ mUVCCamera?.zoom = zoom
+ }
+
+ /**
+ * Get zoom
+ */
+ fun getZoom() = mUVCCamera?.zoom
+
+ /**
+ * Set gain
+ *
+ * @param gain gain value, 0 means reset
+ */
+ fun setGain(gain: Int) {
+ mUVCCamera?.gain = gain
+ }
+
+ /**
+ * Get gain
+ */
+ fun getGain() = mUVCCamera?.gain
+
+ /**
+ * Set gamma
+ *
+ * @param gamma gamma value, 0 means reset
+ */
+ fun setGamma(gamma: Int) {
+ mUVCCamera?.gamma = gamma
+ }
+
+ /**
+ * Get gamma
+ */
+ fun getGamma() = mUVCCamera?.gamma
+
+ /**
+ * Set brightness
+ *
+ * @param brightness brightness value, 0 means reset
+ */
+ fun setBrightness(brightness: Int) {
+ mUVCCamera?.brightness = brightness
+ }
+
+ /**
+ * Get brightness
+ */
+ fun getBrightness() = mUVCCamera?.brightness
+
+ /**
+ * Set contrast
+ *
+ * @param contrast contrast value, 0 means reset
+ */
+ fun setContrast(contrast: Int) {
+ mUVCCamera?.contrast = contrast
+ }
+
+ /**
+ * Get contrast
+ */
+ fun getContrast() = mUVCCamera?.contrast
+
+ /**
+ * Set sharpness
+ *
+ * @param sharpness sharpness value, 0 means reset
+ */
+ fun setSharpness(sharpness: Int) {
+ mUVCCamera?.sharpness = sharpness
+ }
+
+ /**
+ * Get sharpness
+ */
+ fun getSharpness() = mUVCCamera?.sharpness
+
+ /**
+ * Set saturation
+ *
+ * @param saturation saturation value, 0 means reset
+ */
+ fun setSaturation(saturation: Int) {
+ mUVCCamera?.saturation = saturation
+ }
+
+ /**
+ * Get saturation
+ */
+ fun getSaturation() = mUVCCamera?.saturation
+
+ /**
+ * Set hue
+ *
+ * @param hue hue value, 0 means reset
+ */
+ fun setHue(hue: Int) {
+ mUVCCamera?.hue = hue
+ }
+
+ /**
+ * Get hue
+ */
+ fun getHue() = mUVCCamera?.hue
+
+ private fun getUsbDeviceListInternal(): MutableList? {
+ return mUsbMonitor?.getDeviceList(arrayListOf())?.let { devList ->
+ Logger.i(TAG, " find some device list, = $devList")
+ mCacheDeviceList.clear()
+ devList.forEach {
+ // check is camera or need device
+ if (isUsbCamera(it) || isFilterDevice(getContext(), it)) {
+ mCacheDeviceList.add(it)
+ }
+ }
+ mCacheDeviceList
+ }
+ }
+
+ private fun requestCameraPermission(device: UsbDevice?) {
+ if (mRequestPermission.get()) {
+ return
+ }
+ mCacheDeviceList.find {
+ device?.deviceId == it.deviceId
+ }.also { dev ->
+ if (dev == null) {
+ Logger.e(TAG, "open camera failed, not found.")
+ return@also
+ }
+ mRequestPermission.set(true)
+ mUsbMonitor?.requestPermission(dev)
+ }
+ }
+
+ private val frameCallBack = IFrameCallback { frame ->
+ mPreviewDataCbList.forEach { cb ->
+ frame?.apply {
+ val data = ByteArray(capacity())
+ get(data)
+ cb.onPreviewData(data, IPreviewDataCallBack.DataFormat.NV21)
+ if (mNV21DataQueue.size >= MAX_NV21_DATA) {
+ mNV21DataQueue.removeLast()
+ }
+ mNV21DataQueue.offerFirst(data)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "CameraUvc"
+ private const val MIN_FS = 10
+ private const val MAX_FS = 60
+ private const val MAX_NV21_DATA = 5
+ private const val CAPTURE_TIMES_OUT_SEC = 1L
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraxStrategy.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraxStrategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d5c31acf8ad2ff413def91dd9087dec22908773a
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/CameraxStrategy.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera
+
+import android.content.Context
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+
+/** CameraX API
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+class CameraxStrategy(ctx: Context): ICameraStrategy(ctx) {
+ override fun getAllPreviewSizes(aspectRatio: Double?): MutableList {
+ TODO("Not yet implemented")
+ }
+
+ override fun loadCameraInfo() {
+ TODO("Not yet implemented")
+ }
+
+ override fun startPreviewInternal() {
+ TODO("Not yet implemented")
+ }
+
+ override fun stopPreviewInternal() {
+ TODO("Not yet implemented")
+ }
+
+ override fun captureImageInternal(savePath: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun switchCameraInternal(cameraId: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun updateResolutionInternal(width: Int, height: Int) {
+ TODO("Not yet implemented")
+ }
+
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/ICameraStrategy.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/ICameraStrategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e259c851d540b6cca767c143b7088d73da3c7d43
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/ICameraStrategy.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera
+
+import android.Manifest
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.SurfaceTexture
+import android.os.*
+import android.view.OrientationEventListener
+import android.view.SurfaceHolder
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import com.jiangdg.ausbc.callback.ICaptureCallBack
+import com.jiangdg.ausbc.callback.IPreviewDataCallBack
+import com.jiangdg.ausbc.camera.bean.CameraInfo
+import com.jiangdg.ausbc.camera.bean.CameraRequest
+import com.jiangdg.ausbc.camera.bean.CameraStatus
+import com.jiangdg.ausbc.camera.bean.PreviewSize
+import com.jiangdg.ausbc.utils.bus.BusKey
+import com.jiangdg.ausbc.utils.bus.EventBus
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.atomic.AtomicBoolean
+
+/** Camera Manager abstract class
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+abstract class ICameraStrategy(context: Context) : Handler.Callback {
+ private var mThread: HandlerThread? = null
+ private var mCameraHandler: Handler? = null
+ private var mSurfaceTexture: SurfaceTexture? = null
+ private var mSurfaceHolder: SurfaceHolder? = null
+ private var mCameraRequest: CameraRequest? = null
+ private var mContext: Context? = null
+ protected var mPreviewDataCbList = mutableListOf()
+ protected var mCaptureDataCb: ICaptureCallBack? = null
+ protected val mMainHandler: Handler = Handler(Looper.getMainLooper())
+ protected val mSaveImageExecutor: ExecutorService = Executors.newSingleThreadExecutor()
+ protected val mCameraInfoMap = hashMapOf()
+ protected var mIsCapturing: AtomicBoolean = AtomicBoolean(false)
+ protected var mIsPreviewing: AtomicBoolean = AtomicBoolean(false)
+ protected val mDateFormat by lazy {
+ SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault())
+ }
+ protected val mCameraDir by lazy {
+ "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera"
+ }
+
+ private val mDeviceOrientation = object : OrientationEventListener(context) {
+ var orientation = 0
+ private set
+
+ override fun onOrientationChanged(orientation: Int) {
+ this.orientation = orientation
+ }
+ }
+
+ init {
+ this.mContext = context.applicationContext
+ addLifecycleObserver(context)
+ }
+
+ override fun handleMessage(msg: Message): Boolean {
+ when(msg.what) {
+ MSG_INIT -> {
+ loadCameraInfo()
+ }
+ MSG_START_PREVIEW -> {
+ msg.obj ?: return true
+ (msg.obj as CameraRequest).apply {
+ if (mIsPreviewing.get()) {
+ mDeviceOrientation.disable()
+ stopPreviewInternal()
+ }
+ mDeviceOrientation.enable()
+ mCameraRequest = this
+ startPreviewInternal()
+ }
+ }
+ MSG_STOP_PREVIEW -> {
+ mCameraInfoMap.clear()
+ mDeviceOrientation.disable()
+ stopPreviewInternal()
+ }
+ MSG_CAPTURE_IMAGE -> {
+ captureImageInternal(msg.obj as? String)
+ }
+ MSG_SWITCH_CAMERA -> {
+ switchCameraInternal(msg.obj as? String)
+ }
+ }
+ return true
+ }
+
+ /**
+ * Start preview
+ *
+ * @param request camera quest, see [CameraRequest]
+ * @param renderSurface [SurfaceHolder] or [SurfaceTexture]
+ */
+ @Synchronized
+ fun startPreview(request: CameraRequest?, renderSurface: T?) {
+ if (mIsPreviewing.get() || mThread?.isAlive == true) {
+ stopPreview()
+ }
+ if (mCameraRequest == null && request == null) {
+ throw IllegalStateException("camera request can't be null")
+ }
+ if (mSurfaceHolder == null && mSurfaceTexture == null && renderSurface == null) {
+ throw IllegalStateException("render surface can't be null")
+ }
+ when (renderSurface) {
+ is SurfaceTexture -> {
+ setSurfaceTexture(renderSurface)
+ }
+ is SurfaceHolder -> {
+ setSurfaceHolder(renderSurface)
+ }
+ else -> {
+ }
+ }.also {
+ val thread = HandlerThread(THREAD_NAME).apply {
+ start()
+ }.also {
+ mCameraHandler = Handler(it.looper, this)
+ mCameraHandler?.obtainMessage(MSG_INIT)?.sendToTarget()
+ mCameraHandler?.obtainMessage(MSG_START_PREVIEW, request ?: mCameraRequest)?.sendToTarget()
+ }
+ this.mThread = thread
+ }
+ }
+
+ /**
+ * Stop preview
+ */
+ @Synchronized
+ fun stopPreview() {
+ mThread ?: return
+ mCameraHandler ?: return
+ mCameraHandler?.obtainMessage(MSG_STOP_PREVIEW)?.sendToTarget()
+ mThread?.quitSafely()
+ mThread = null
+ mCameraHandler = null
+ }
+
+ /**
+ * Capture image
+ *
+ * @param callBack capture status, see [ICaptureCallBack]
+ * @param savePath image save path
+ */
+ @Synchronized
+ fun captureImage(callBack: ICaptureCallBack, savePath: String?) {
+ this.mCaptureDataCb = callBack
+ mCameraHandler?.obtainMessage(MSG_CAPTURE_IMAGE, savePath)?.sendToTarget()
+ }
+
+ /**
+ * Switch camera
+ *
+ * @param cameraId camera id, camera1/camera2/camerax is null
+ */
+ @Synchronized
+ fun switchCamera(cameraId: String? = null) {
+ mCameraHandler?.obtainMessage(MSG_SWITCH_CAMERA, cameraId)?.sendToTarget()
+ }
+
+ private fun setSurfaceTexture(surfaceTexture: SurfaceTexture) {
+ this.mSurfaceTexture = surfaceTexture
+ }
+
+ private fun setSurfaceHolder(holder: SurfaceHolder) {
+ this.mSurfaceHolder = holder
+ }
+
+ /**
+ * Get all preview sizes
+ *
+ * @param aspectRatio preview size aspect ratio
+ * @return preview size list
+ */
+ abstract fun getAllPreviewSizes(aspectRatio: Double? = null): MutableList?
+
+ /**
+ * Get surface texture
+ *
+ * @return camera render [SurfaceTexture]
+ */
+ fun getSurfaceTexture(): SurfaceTexture? = mSurfaceTexture
+
+ /**
+ * Get surface holder
+ *
+ * @return camera render [SurfaceHolder]
+ */
+ fun getSurfaceHolder(): SurfaceHolder? = mSurfaceHolder
+
+ /**
+ * Get context
+ *
+ * @return context
+ */
+ protected fun getContext(): Context? = mContext
+
+ /**
+ * Get request
+ *
+ * @return camera request, see [CameraRequest]
+ */
+ protected fun getRequest(): CameraRequest? = mCameraRequest
+
+ /**
+ * Get camera handler
+ *
+ * @return camera thread handler, see [HandlerThread]
+ */
+ protected fun getCameraHandler(): Handler? = mCameraHandler
+
+ /**
+ * Get device orientation
+ *
+ * @return device orientation angle
+ */
+ protected fun getDeviceOrientation(): Int = mDeviceOrientation.orientation
+
+ /**
+ * Post camera status
+ *
+ * @param status see [CameraStatus]
+ */
+ protected fun postCameraStatus(status: CameraStatus) {
+ EventBus.with(BusKey.KEY_CAMERA_STATUS).postMessage(status)
+ }
+
+ /**
+ * Register uvc camera monitor, see [CameraUvcStrategy]
+ */
+ open fun register() {}
+
+ /**
+ * Un register uvc camera monitor, see [CameraUvcStrategy]
+ */
+ open fun unRegister() {}
+
+ /**
+ * Load camera info,
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ */
+ protected abstract fun loadCameraInfo()
+
+ /**
+ * Start preview internal,
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ */
+ protected abstract fun startPreviewInternal()
+
+ /**
+ * Stop preview internal,
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ */
+ protected abstract fun stopPreviewInternal()
+
+ /**
+ * Capture image internal,
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @param savePath
+ */
+ protected abstract fun captureImageInternal(savePath: String?)
+
+ /**
+ * Switch camera internal,
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @param cameraId camera id. only uvc camera used
+ */
+ protected abstract fun switchCameraInternal(cameraId: String?)
+
+ /**
+ * Update resolution internal
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @param width
+ * @param height
+ */
+ protected abstract fun updateResolutionInternal(width: Int, height: Int)
+
+ /**
+ * Has camera permission
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @return true was granted
+ */
+ protected fun hasCameraPermission(): Boolean{
+ getContext() ?: return false
+ val locPermission = ContextCompat.checkSelfPermission(getContext()!!, Manifest.permission.CAMERA)
+ return locPermission == PackageManager.PERMISSION_GRANTED
+ }
+
+ /**
+ * Has storage permission
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @return true was granted
+ */
+ protected fun hasStoragePermission(): Boolean {
+ getContext() ?: return false
+ val locPermission = ContextCompat.checkSelfPermission(getContext()!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ return locPermission == PackageManager.PERMISSION_GRANTED
+ }
+
+ private fun addLifecycleObserver(context: Context) {
+ if (context !is LifecycleOwner) return
+ context.lifecycle.addObserver(object : LifecycleEventObserver {
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ when (event) {
+ Lifecycle.Event.ON_CREATE -> {
+ register()
+ }
+ Lifecycle.Event.ON_DESTROY -> {
+ stopPreview()
+ unRegister()
+ }
+ else -> {}
+ }
+ }
+ })
+ }
+
+ /**
+ * Add preview data call back
+ * see [Camera1Strategy] or [Camera2Strategy] or [CameraUvcStrategy]
+ * @param callBack preview data call back
+ */
+ fun addPreviewDataCallBack(callBack: IPreviewDataCallBack) {
+ if (mPreviewDataCbList.contains(callBack)) {
+ return
+ }
+ mPreviewDataCbList.add(callBack)
+ }
+
+ /**
+ * check camera opened
+ *
+ * @return camera open status, true or false
+ */
+ fun isCameraOpened() = mIsPreviewing.get()
+
+ companion object {
+ private const val TAG = "ICameraStrategy"
+ private const val THREAD_NAME = "camera_manager"
+ private const val MSG_INIT = 0x00
+ private const val MSG_START_PREVIEW = 0x01
+ private const val MSG_STOP_PREVIEW = 0x02
+ private const val MSG_CAPTURE_IMAGE = 0x03
+ private const val MSG_SWITCH_CAMERA = 0x04
+
+ internal const val TYPE_FRONT = 0
+ internal const val TYPE_BACK = 1
+ internal const val TYPE_OTHER = 2
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraInfo.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3488db6177bcfd3cb81b8415cb967fb464dd40c9
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraInfo.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+/** Camera device information
+ *
+ * @author Created by jiangdg on 2021/12/23
+ */
+open class CameraInfo(open val cameraId: String) {
+ var cameraPreviewSizes: MutableList? = null
+ var cameraVid: Int = 0
+ var cameraPid: Int = 0
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraRequest.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ebe4a98e5883a83a05bb882c77180223a94b86c5
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraRequest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+import androidx.annotation.Keep
+
+
+/** Camera request parameters
+ *
+ * @author Created by jiangdg on 2021/12/20
+ */
+@Keep
+class CameraRequest private constructor() {
+ var previewWidth: Int = DEFAULT_WIDTH
+ var previewHeight: Int = DEFAULT_HEIGHT
+ var cameraId: String = ""
+ var isFrontCamera: Boolean = false
+ var isContinuousAFModel: Boolean = true
+ private set
+ var isContinuousAEModel: Boolean = true
+ private set
+
+ override fun toString(): String {
+ return "CameraRequest(previewWidth=$previewWidth, previewHeight=$previewHeight, " +
+ "cameraId='$cameraId', isFrontCamera=$isFrontCamera, " +
+ "isContinuousAFModel=$isContinuousAFModel, isContinuousAEModel=$isContinuousAEModel)"
+ }
+
+ /**
+ * Camera request builder
+ *
+ * @constructor Create empty Camera request builder
+ */
+ class Builder {
+ private val mRequest by lazy {
+ CameraRequest()
+ }
+
+ /**
+ * Set preview width
+ *
+ * @param width camera preview width
+ * @return see [Builder]
+ */
+ fun setPreviewWidth(width: Int): Builder {
+ mRequest.previewWidth = width
+ return this
+ }
+
+ /**
+ * Set preview height
+ *
+ * @param height camera preview height
+ * @return [Builder]
+ */
+ fun setPreviewHeight(height: Int): Builder {
+ mRequest.previewHeight = height
+ return this
+ }
+
+ /**
+ * Set camera id
+ *
+ * @param cameraId camera id
+ * @return [Builder]
+ */
+ fun setCameraId(cameraId: String): Builder {
+ mRequest.cameraId = cameraId
+ return this
+ }
+
+ /**
+ * Set front camera
+ *
+ * @param isFrontCamera front camera flag
+ * @return [Builder]
+ */
+ fun setFrontCamera(isFrontCamera: Boolean): Builder {
+ mRequest.isFrontCamera = isFrontCamera
+ return this
+ }
+
+ /**
+ * Set continuous a f model
+ *
+ * @param isContinuousAF
+ * @return [Builder]
+ */
+ fun setContinuousAFModel(isContinuousAF: Boolean): Builder {
+ mRequest.isContinuousAFModel = isContinuousAF
+ return this
+ }
+
+ /**
+ * Set continuous auto model
+ *
+ * @param isContinuousAuto
+ * @return [Builder]
+ */
+ fun setContinuousAutoModel(isContinuousAuto: Boolean): Builder {
+ mRequest.isContinuousAEModel = isContinuousAuto
+ return this
+ }
+
+ /**
+ * Create a CameraRequest
+ *
+ * @return [CameraRequest]
+ */
+ fun create(): CameraRequest {
+ return mRequest
+ }
+ }
+
+ companion object {
+ private const val DEFAULT_WIDTH = 640
+ private const val DEFAULT_HEIGHT = 480
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraStatus.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraStatus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..70429069e5d99d198be0f2cd34e4dfd732b702cc
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraStatus.kt
@@ -0,0 +1,17 @@
+package com.jiangdg.ausbc.camera.bean
+
+import androidx.annotation.Keep
+
+/** Camera status
+ *
+ * @author Created by jiangdg on 2022/4/5
+ */
+@Keep
+data class CameraStatus(val code: Int, val message: String? = null) {
+ companion object {
+ const val START = 1
+ const val STOP = 2
+ const val ERROR = -1
+ const val ERROR_PREVIEW_SIZE = -2
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraUvcInfo.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraUvcInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..da522a15059d6955530a6d3bb7736c38be81f5ff
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraUvcInfo.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+/** uvc camera info
+ *
+ * @author Created by jiangdg on 2022/1/27
+ */
+class CameraUvcInfo(override val cameraId: String) : CameraInfo(cameraId) {
+ var cameraName: String = ""
+ var cameraProductName: String? = null
+ var cameraManufacturerName: String? = null
+ var cameraProtocol: Int = 0
+ var cameraClass: Int = 0
+ var cameraSubClass: Int = 0
+
+ override fun toString(): String {
+ return "CameraUvcInfo(cameraId='$cameraId', " +
+ "cameraName='$cameraName', " +
+ "cameraProductName='$cameraProductName', " +
+ "cameraManufacturerName='$cameraManufacturerName', " +
+ "cameraProtocol=$cameraProtocol, " +
+ "cameraClass=$cameraClass, " +
+ "cameraSubClass=$cameraSubClass, " +
+ "cameraVid=$cameraVid, " +
+ "cameraPid=$cameraPid, " +
+ "cameraPreviewSizes=$cameraPreviewSizes)"
+
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV1Info.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV1Info.kt
new file mode 100644
index 0000000000000000000000000000000000000000..848ae3adf869f952727e27000e99a08014e36def
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV1Info.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+/** camera 1 info
+ *
+ * @author Created by jiangdg on 2022/1/27
+ */
+data class CameraV1Info(override val cameraId: String) : CameraInfo(cameraId) {
+ var cameraType: Int = 0
+
+ override fun toString(): String {
+ return "CameraV1Info(cameraId='$cameraId', " +
+ "cameraType=$cameraType)"
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV2Info.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV2Info.kt
new file mode 100644
index 0000000000000000000000000000000000000000..467c0b88e10a7994cee54c3100c5658013271709
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/CameraV2Info.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+import android.hardware.camera2.CameraCharacteristics
+
+/**
+ *
+ * @author Created by jiangdg on 2022/1/27
+ */
+data class CameraV2Info(override val cameraId: String) : CameraInfo(cameraId) {
+ var cameraType: Int = 0
+ var cameraCharacteristics: CameraCharacteristics? = null
+
+ override fun toString(): String {
+ return "CameraV2Info(cameraId='$cameraId', " +
+ "cameraType=$cameraType, " +
+ "cameraCharacteristics=$cameraCharacteristics)"
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/PreviewSize.kt b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/PreviewSize.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c7c8e469be3d871ed1092adcf90311015cd46767
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/camera/bean/PreviewSize.kt
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.camera.bean
+
+/** Camera preview size
+ *
+ * @author Created by jiangdg on 2021/12/24
+ */
+data class PreviewSize(val width: Int = 0, val height: Int = 0)
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/encode/AACEncodeProcessor.kt b/libausbc/src/main/java/com/jiangdg/ausbc/encode/AACEncodeProcessor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c6cbfd9ec2afb29680767dc0277881496346bf51
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/encode/AACEncodeProcessor.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.encode
+
+import android.media.*
+import android.os.Process
+import com.jiangdg.ausbc.callback.ICaptureCallBack
+import com.jiangdg.ausbc.callback.IPlayCallBack
+import com.jiangdg.ausbc.encode.bean.RawData
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import com.jiangdg.ausbc.utils.Utils
+import com.jiangdg.natives.LameMp3
+import java.io.File
+import java.io.FileOutputStream
+import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.Exception
+
+/** AAC encode by MediaCodec
+ *
+ * @author Created by jiangdg on 2022/2/10
+ */
+class AACEncodeProcessor : AbstractProcessor() {
+ private var mAudioTrack: AudioTrack? = null
+ private var mAudioRecord: AudioRecord? = null
+ private var mPresentationTimeUs: Long = 0L
+ private val mPlayQueue: ConcurrentLinkedQueue by lazy {
+ ConcurrentLinkedQueue()
+ }
+ private val mRecordMp3Queue: ConcurrentLinkedQueue by lazy {
+ ConcurrentLinkedQueue()
+ }
+ private val mAudioThreadPool: ExecutorService by lazy {
+ Executors.newFixedThreadPool(3)
+ }
+ private val mAudioRecordState: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+ private val mAudioPlayState: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+ private val mRecordMp3State: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+ private val mBufferSize: Int by lazy {
+ AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_IN_CONFIG, AUDIO_FORMAT_16BIT)
+ }
+
+ override fun getThreadName(): String = TAG
+
+ override fun handleStartEncode() {
+ initAudioRecord()
+ try {
+ MediaFormat().apply {
+ setString(MediaFormat.KEY_MIME, MIME_TYPE)
+ setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE)
+ setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNEL_COUNT)
+ setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE)
+ setInteger(MediaFormat.KEY_AAC_PROFILE, CODEC_AAC_PROFILE)
+ setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, MAX_INPUT_SIZE)
+ }.also { format ->
+ mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE)
+ mMediaCodec?.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
+ mMediaCodec?.start()
+ mEncodeState.set(true)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "init aac media codec success.")
+ }
+ }
+ doEncodeData()
+ } catch (e: Exception) {
+ Logger.e(TAG, "init aac media codec failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ override fun handleStopEncode() {
+ try {
+ mEncodeState.set(false)
+ mMediaCodec?.stop()
+ mMediaCodec?.release()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "release aac media codec success.")
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "release aac media codec failed, err = ${e.localizedMessage}", e)
+ } finally {
+ releaseAudioRecord()
+ mRawDataQueue.clear()
+ mMediaCodec = null
+ }
+ }
+
+ override fun getPTSUs(bufferSize: Int): Long {
+ //A frame of audio frame size int size = sampling rate * bit width * sampling time * number of channels
+ // 1s timestamp calculation formula presentationTimeUs = 1000000L * (totalBytes / sampleRate/ audioFormat / channelCount / 8 )
+ //totalBytes : total size of incoming encoder
+ //1000 000L : The unit is microseconds, after conversion = 1s,
+ //Divided by 8: The original unit of pcm is bit, 1 byte = 8 bit, 1 short = 16 bit, and it needs to be converted if it is carried by Byte[] and Short[]
+ mPresentationTimeUs += (1.0 * bufferSize / (SAMPLE_RATE * CHANNEL_COUNT * (AUDIO_FORMAT_BITS / 8)) * 1000000.0).toLong()
+ return mPresentationTimeUs
+ }
+
+ /**
+ * Play audio start
+ *
+ * @param callBack play status call back, see [IPlayCallBack]
+ */
+ fun playAudioStart(callBack: IPlayCallBack?) {
+ mAudioThreadPool.submit {
+ try {
+ initAudioRecord()
+ initAudioTrack()
+ mMainHandler.post {
+ callBack?.onBegin()
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "start play mic success.")
+ }
+ while (mAudioPlayState.get()) {
+ val state = mAudioTrack?.state
+ if (state != AudioTrack.STATE_INITIALIZED) {
+ break
+ }
+ mPlayQueue.poll()?.apply {
+ mAudioTrack?.play()
+ mAudioTrack?.write(data, 0, size)
+ }
+ }
+ releaseAudioTrack()
+ releaseAudioRecord()
+ mMainHandler.post {
+ callBack?.onComplete()
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "stop play mic success.")
+ }
+ } catch (e: Exception) {
+ mMainHandler.post {
+ callBack?.onError(e.localizedMessage?: "unknown exception")
+ }
+ Logger.e(TAG, "start/stop play mic failed, err = ${e.localizedMessage}", e)
+ }
+ }
+ }
+
+ /**
+ * Play audio stop
+ */
+ fun playAudioStop() {
+ mAudioPlayState.set(false)
+ }
+
+ /**
+ * Record mp3start
+ *
+ * @param audioPath custom mp4 record saving path, default is [/data/data/packagename/files]
+ * @param callBack record status, see [ICaptureCallBack]
+ */
+ fun recordMp3Start(audioPath: String?, callBack: ICaptureCallBack) {
+ mAudioThreadPool.submit {
+ var fos: FileOutputStream? = null
+ try {
+ if (audioPath.isNullOrEmpty()) {
+ mMainHandler.post {
+ callBack.onError("save path($audioPath) invalid")
+ }
+ return@submit
+ }
+ initAudioRecord()
+ val file = File(audioPath)
+ if (file.exists()) {
+ file.delete()
+ }
+ fos = FileOutputStream(file)
+ val mp3Buf = ByteArray(1024)
+ LameMp3.lameInit(SAMPLE_RATE, CHANNEL_COUNT, SAMPLE_RATE, BIT_RATE, DEGREE_RECORD_MP3)
+ mMainHandler.post {
+ callBack.onBegin()
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "start record mp3 success, path = $audioPath")
+ }
+ mRecordMp3State.set(true)
+ while (mRecordMp3State.get()) {
+ mRecordMp3Queue.poll()?.apply {
+ val tmpData = MediaUtils.transferByte2Short(data, size)
+ val encodeSize = LameMp3.lameEncode(tmpData, null, tmpData.size, mp3Buf)
+ if (encodeSize > 0) {
+ fos?.write(mp3Buf, 0, encodeSize)
+ }
+ }
+ }
+ val flushSize = LameMp3.lameFlush(mp3Buf)
+ if (flushSize > 0) {
+ fos.write(mp3Buf, 0, flushSize)
+ }
+ } catch (e: Exception) {
+ mMainHandler.post {
+ callBack.onError(e.localizedMessage?: "unknown exception")
+ }
+ Logger.e(TAG, "start/stop record mp3 failed, err = ${e.localizedMessage}", e)
+ } finally {
+ try {
+ fos?.close()
+ fos = null
+ LameMp3.lameClose()
+ releaseAudioRecord()
+ mMainHandler.post {
+ callBack.onComplete(audioPath)
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "stop record mp3 success.")
+ }
+ } catch (e: Exception) {
+ mMainHandler.post {
+ callBack.onError(e.localizedMessage?: "unknown exception")
+ }
+ Logger.e(TAG, "stop record mp3 failed, err = ${e.localizedMessage}", e)
+ }
+ }
+ }
+ }
+
+ /**
+ * Record mp3stop
+ */
+ fun recordMp3Stop() {
+ mRecordMp3State.set(false)
+ }
+
+ private fun initAudioRecord() {
+ if (mAudioRecordState.get()) return
+ mAudioThreadPool.submit {
+ initAudioRecordInternal()
+ while (mAudioRecordState.get()) {
+ val recordingState = mAudioRecord?.recordingState
+ if (recordingState != AudioRecord.RECORDSTATE_RECORDING) {
+ Logger.e(TAG, "initAudioRecord failed, state = $recordingState")
+ break
+ }
+ val data = ByteArray(mBufferSize)
+ val readBytes = mAudioRecord?.read(data, 0, mBufferSize) ?: 0
+ if (readBytes <= 0) {
+ continue
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "read pcm data, len = $readBytes")
+ }
+ // pcm encode queue
+ if (mRawDataQueue.size >= MAX_QUEUE_SIZE) {
+ mRawDataQueue.poll()
+ }
+ mRawDataQueue.offer(RawData(data, readBytes))
+ // pcm play queue
+ if (mPlayQueue.size >= MAX_QUEUE_SIZE) {
+ mPlayQueue.poll()
+ }
+ mPlayQueue.offer(RawData(data, readBytes))
+ // pcm to mp3 queue
+ if (mRecordMp3Queue.size >= MAX_QUEUE_SIZE) {
+ mRecordMp3Queue.poll()
+ }
+ mRecordMp3Queue.offer(RawData(data, readBytes))
+ }
+ releaseAudioRecordInternal()
+ }
+ }
+
+ private fun releaseAudioRecord() {
+ if (mEncodeState.get() || mAudioPlayState.get() || mRecordMp3State.get()) {
+ return
+ }
+ mAudioRecordState.set(false)
+ }
+
+ private fun initAudioRecordInternal() {
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO)
+ mAudioRecord = AudioRecord(
+ AUDIO_RECORD_SOURCE, SAMPLE_RATE,
+ CHANNEL_IN_CONFIG, AUDIO_FORMAT_16BIT, mBufferSize
+ )
+ mAudioRecord?.startRecording()
+ mAudioRecordState.set(true)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "initAudioRecordInternal success")
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "initAudioRecordInternal failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ private fun releaseAudioRecordInternal() {
+ try {
+ mAudioRecord?.stop()
+ mAudioRecord?.release()
+ mAudioRecord = null
+ mRawDataQueue.clear()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "releaseAudioRecordInternal success.")
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "releaseAudioRecordInternal failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ private fun initAudioTrack() {
+ if (mAudioPlayState.get()) {
+ Logger.w(TAG, "initAudioTracker has ready execute!")
+ return
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)
+ val minBufferSize = AudioTrack.getMinBufferSize(
+ SAMPLE_RATE,
+ CHANNEL_OUT_CONFIG,
+ AUDIO_FORMAT_16BIT
+ )
+ mAudioTrack = AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ SAMPLE_RATE,
+ CHANNEL_OUT_CONFIG,
+ AUDIO_FORMAT_16BIT,
+ minBufferSize,
+ AUDIO_TRACK_MODE
+ )
+ mAudioPlayState.set(true)
+ }
+
+ private fun releaseAudioTrack() {
+ try {
+ mAudioTrack?.release()
+ mAudioTrack = null
+ } catch (e: Exception) {
+ Logger.e(TAG, "releaseAudioTracker failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ companion object {
+ private const val TAG = "AACEncodeProcessor"
+ private const val MIME_TYPE = "audio/mp4a-latm"
+ private const val SAMPLE_RATE = 8000
+ private const val BIT_RATE = 16000
+ private const val MAX_INPUT_SIZE = 8192
+ private const val CHANNEL_COUNT = 1
+ private const val CHANNEL_IN_CONFIG = AudioFormat.CHANNEL_IN_MONO
+ private const val CHANNEL_OUT_CONFIG = AudioFormat.CHANNEL_OUT_MONO
+ private const val AUDIO_FORMAT_16BIT = AudioFormat.ENCODING_PCM_16BIT
+ private const val AUDIO_TRACK_MODE = AudioTrack.MODE_STREAM
+ private const val AUDIO_RECORD_SOURCE = MediaRecorder.AudioSource.MIC
+ private const val CODEC_AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC
+ private const val AUDIO_FORMAT_BITS = 16
+ private const val DEGREE_RECORD_MP3 = 7
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/encode/AbstractProcessor.kt b/libausbc/src/main/java/com/jiangdg/ausbc/encode/AbstractProcessor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ed6e98ba6a7ea58395d0e93b473759a0e90f5bc4
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/encode/AbstractProcessor.kt
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.encode
+
+import android.media.MediaCodec
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import com.jiangdg.ausbc.callback.IEncodeDataCallBack
+import com.jiangdg.ausbc.encode.bean.RawData
+import com.jiangdg.ausbc.encode.muxer.Mp4Muxer
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.Utils
+import java.lang.Exception
+import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.concurrent.atomic.AtomicBoolean
+
+
+/** Abstract processor
+ *
+ * @author Created by jiangdg on 2022/2/10
+ */
+abstract class AbstractProcessor(private val gLESRender: Boolean = false) {
+ private var mEncodeThread: HandlerThread? = null
+ private var mEncodeHandler: Handler? = null
+ protected var mMediaCodec: MediaCodec? = null
+ private var mMp4Muxer: Mp4Muxer? = null
+ private var isVideo: Boolean = false
+ private var mEncodeDataCb: IEncodeDataCallBack? = null
+ protected val mRawDataQueue: ConcurrentLinkedQueue = ConcurrentLinkedQueue()
+ protected var mMainHandler: Handler = Handler(Looper.getMainLooper())
+
+ protected val mEncodeState: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+
+ /**
+ * Start encode
+ *
+ * [getThreadName] diff audio thread or video thread
+ */
+ fun startEncode() {
+ mEncodeThread = HandlerThread(this.getThreadName())
+ mEncodeThread?.start()
+ mEncodeHandler = Handler(mEncodeThread!!.looper) { msg ->
+ when (msg.what) {
+ MSG_START -> {
+ handleStartEncode()
+ }
+ MSG_STOP -> {
+ handleStopEncode()
+ }
+ }
+ true
+ }
+ mEncodeHandler?.obtainMessage(MSG_START)?.sendToTarget()
+ }
+
+ /**
+ * Stop encode
+ */
+ fun stopEncode() {
+ mEncodeState.set(false)
+ mEncodeHandler?.obtainMessage(MSG_STOP)?.sendToTarget()
+ mEncodeThread?.quitSafely()
+ mEncodeThread = null
+ mEncodeHandler = null
+ mEncodeDataCb = null
+ }
+
+ /**
+ * Add encode data call back
+ *
+ * @param callBack aac or h264 data call back, see [IEncodeDataCallBack]
+ */
+ fun addEncodeDataCallBack(callBack: IEncodeDataCallBack?) {
+ this.mEncodeDataCb = callBack
+ }
+
+ /**
+ * Set mp4muxer
+ *
+ * @param muxer mp4 media muxer
+ * @param isVideo data type, audio or video
+ */
+ @Synchronized
+ fun setMp4Muxer(muxer: Mp4Muxer, isVideo: Boolean) {
+ this.mMp4Muxer = muxer
+ this.isVideo = isVideo
+ }
+
+ /**
+ * Put raw data
+ *
+ * @param data media data, pcm or yuv
+ */
+ fun putRawData(data: RawData) {
+ if (! mEncodeState.get()) {
+ return
+ }
+ if (mRawDataQueue.size >= MAX_QUEUE_SIZE) {
+ mRawDataQueue.poll()
+ }
+ mRawDataQueue.offer(data)
+ }
+
+ /**
+ * Is encoding
+ */
+ fun isEncoding() = mEncodeState.get()
+
+ /**
+ * Get thread name
+ *
+ * @return Get encode thread name
+ */
+ protected abstract fun getThreadName(): String
+
+ /**
+ * Handle start encode
+ */
+ protected abstract fun handleStartEncode()
+
+ /**
+ * Handle stop encode
+ */
+ protected abstract fun handleStopEncode()
+
+ /**
+ * Get presentation time
+ *
+ * @param bufferSize buffer size
+ * @return presentation time in us
+ */
+ protected abstract fun getPTSUs(bufferSize: Int): Long
+
+ /**
+ * Is lower lollipop
+ */
+ protected fun isLowerLollipop() = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
+
+ /**
+ * Do encode data
+ */
+ protected fun doEncodeData() {
+ while (mEncodeState.get()) {
+ try {
+ queueFrameIfNeed()
+ val bufferInfo = MediaCodec.BufferInfo()
+ var outputIndex = 0
+ do {
+ mMediaCodec?.let { codec ->
+ outputIndex = codec.dequeueOutputBuffer(bufferInfo, TIMES_OUT_US)
+ when (outputIndex) {
+ MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "addTracker is video = $isVideo")
+ }
+ mMp4Muxer?.addTracker(mMediaCodec?.outputFormat, isVideo)
+ }
+ else -> {
+ if (outputIndex < 0) {
+ return@let
+ }
+ val outputBuffer = if (isLowerLollipop()) {
+ codec.outputBuffers[outputIndex]
+ } else {
+ codec.getOutputBuffer(outputIndex)
+ }
+ if (outputBuffer != null) {
+ val encodeData = ByteArray(bufferInfo.size)
+ outputBuffer.get(encodeData)
+ val type = if (isVideo) {
+ IEncodeDataCallBack.DataType.H264
+ } else {
+ IEncodeDataCallBack.DataType.AAC
+ }
+ mEncodeDataCb?.onEncodeData(encodeData, encodeData.size, type)
+ mMp4Muxer?.pumpStream(outputBuffer, bufferInfo, isVideo)
+ logSpecialFrame(bufferInfo, encodeData.size)
+ }
+ codec.releaseOutputBuffer(outputIndex, false)
+ }
+ }
+ }
+ } while (outputIndex >= 0)
+ } catch (e: Exception) {
+ Logger.e(TAG, "doEncodeData failed, video = ${isVideo}, err = ${e.localizedMessage}", e)
+ }
+ }
+ }
+
+ private fun queueFrameIfNeed() {
+ mMediaCodec?.let { codec ->
+ if (mRawDataQueue.isEmpty()) {
+ return@let
+ }
+ if (gLESRender && isVideo) {
+ return@let
+ }
+ val rawData = mRawDataQueue.poll() ?: return@let
+ val inputIndex = codec.dequeueInputBuffer(TIMES_OUT_US)
+ if (inputIndex < 0) {
+ return@let
+ }
+ val inputBuffer = if (isLowerLollipop()) {
+ codec.inputBuffers[inputIndex]
+ } else {
+ codec.getInputBuffer(inputIndex)
+ }
+ inputBuffer?.clear()
+ inputBuffer?.put(rawData.data)
+ codec.queueInputBuffer(inputIndex, 0, rawData.data.size, getPTSUs(rawData.data.size), 0)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "queue mediacodec data, isVideo=$isVideo, len=${rawData.data.size}")
+ }
+ }
+ }
+
+ private fun logSpecialFrame(bufferInfo: MediaCodec.BufferInfo, length: Int) {
+ if (length == 0 || !isVideo) {
+ return
+ }
+ if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
+ Logger.i(TAG, "isVideo = $isVideo, Key frame, len = $length")
+ } else if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
+ Logger.i(TAG, "isVideo = $isVideo, Pps/sps frame, len = $length")
+ }
+ }
+
+ companion object {
+ private const val TAG = "AbstractProcessor"
+ private const val MSG_START = 1
+ private const val MSG_STOP = 2
+ private const val TIMES_OUT_US = 10000L
+
+ const val MAX_QUEUE_SIZE = 10
+ }
+
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/encode/H264EncodeProcessor.kt b/libausbc/src/main/java/com/jiangdg/ausbc/encode/H264EncodeProcessor.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a3f5284aa0d23092dac30bc47af9e85de7f5b5fa
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/encode/H264EncodeProcessor.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.encode
+
+import android.media.MediaCodec
+import android.media.MediaCodecInfo
+import android.media.MediaFormat
+import android.view.Surface
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.Utils
+import java.lang.Exception
+
+/**
+ * Encode h264 by MediaCodec
+ *
+ * @property width yuv width
+ * @property height yuv height
+ * @property gLESRender rendered by opengl flag
+ * @author Created by jiangdg on 2022/2/10
+ */
+class H264EncodeProcessor(
+ val width: Int,
+ val height: Int,
+ private val gLESRender: Boolean = true
+) : AbstractProcessor(gLESRender) {
+
+ private var mFrameRate: Int? = null
+ private var mBitRate: Int? = null
+ private var mReadyListener: OnEncodeReadyListener? = null
+
+ override fun getThreadName(): String = TAG
+
+ override fun handleStartEncode() {
+ try {
+ val mediaFormat = MediaFormat.createVideoFormat(MIME, width, height)
+ mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate ?: FRAME_RATE)
+ mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate ?: getEncodeBitrate(width, height))
+ mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_FRAME_INTERVAL)
+ mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, getSupportColorFormat())
+ mMediaCodec = MediaCodec.createEncoderByType(MIME)
+ mMediaCodec?.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
+ if (gLESRender) {
+ mReadyListener?.onReady(mMediaCodec?.createInputSurface())
+ }
+ mMediaCodec?.start()
+ mEncodeState.set(true)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "init h264 media codec success.")
+ }
+ doEncodeData()
+ } catch (e: Exception) {
+ Logger.e(TAG, "start h264 media codec failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ override fun handleStopEncode() {
+ try {
+ mEncodeState.set(false)
+ mMediaCodec?.stop()
+ mMediaCodec?.release()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "release h264 media codec success.")
+ }
+ } catch (e: Exception) {
+ Logger.e(TAG, "Stop mediaCodec failed, err = ${e.localizedMessage}", e)
+ } finally {
+ mRawDataQueue.clear()
+ mMediaCodec = null
+ }
+ }
+
+ override fun getPTSUs(bufferSize: Int): Long = System.nanoTime() / 1000L
+
+ /**
+ * Set on encode ready listener
+ *
+ * @param listener input surface ready listener
+ */
+ fun setOnEncodeReadyListener(listener: OnEncodeReadyListener) {
+ this.mReadyListener = listener
+ }
+
+ private fun getSupportColorFormat(): Int {
+ if (gLESRender) {
+ return MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
+ }
+ return MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
+ }
+
+ private fun getEncodeBitrate(width: Int, height: Int): Int {
+ var bitRate = width * height * 20 * 3 * 0.07F
+ if (width >= 1920 || height >= 1920) {
+ bitRate *= 0.75F
+ } else if (width >= 1280 || height >= 1280) {
+ bitRate *= 1.2F
+ } else if (width >= 640 || height >= 640) {
+ bitRate *= 1.4F
+ }
+ return bitRate.toInt()
+ }
+
+ /**
+ * Set encode rate
+ *
+ * @param bitRate encode bit rate, kpb/s
+ * @param frameRate encode frame rate, fp/s
+ */
+ fun setEncodeRate(bitRate: Int?, frameRate: Int?) {
+ this.mBitRate = bitRate
+ this.mFrameRate = frameRate
+ }
+
+ /**
+ * On encode ready listener
+ */
+ interface OnEncodeReadyListener {
+ /**
+ * On ready
+ *
+ * @param surface mediacodec input surface for getting raw data
+ */
+ fun onReady(surface: Surface?)
+ }
+
+ companion object {
+ private const val TAG = "H264EncodeProcessor"
+ private const val MIME = "video/avc"
+ private const val FRAME_RATE = 30
+ private const val KEY_FRAME_INTERVAL = 1
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/encode/bean/RawData.kt b/libausbc/src/main/java/com/jiangdg/ausbc/encode/bean/RawData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3fd27b8c1a8e578537e9748e183e8c8bfeec2800
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/encode/bean/RawData.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.encode.bean
+
+import androidx.annotation.Keep
+
+/**
+ * PCM or YUV raw data
+ *
+ * @property data media data, pcm or yuv
+ * @property size media data size
+ * @constructor Create empty Raw data
+ *
+ * @author Created by jiangdg on 2022/2/10
+ */
+@Keep
+data class RawData(val data: ByteArray, val size: Int) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as RawData
+
+ if (!data.contentEquals(other.data)) return false
+ if (size != other.size) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = data.contentHashCode()
+ result = 31 * result + size
+ return result
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/encode/muxer/Mp4Muxer.kt b/libausbc/src/main/java/com/jiangdg/ausbc/encode/muxer/Mp4Muxer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fdee3c2fbdf12772dad934b3c2251b47ff659389
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/encode/muxer/Mp4Muxer.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.encode.muxer
+
+import android.content.ContentValues
+import android.content.Context
+import android.media.MediaCodec
+import android.media.MediaFormat
+import android.media.MediaMuxer
+import android.os.Environment
+import android.os.Handler
+import android.os.Looper
+import android.provider.MediaStore
+import android.text.format.DateUtils
+import com.jiangdg.ausbc.callback.ICaptureCallBack
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import com.jiangdg.ausbc.utils.Utils
+import java.io.File
+import java.lang.Exception
+import java.nio.ByteBuffer
+import java.text.SimpleDateFormat
+import java.util.*
+
+/**
+ * MediaMuxer for Mp4
+ *
+ * @property path mp4 saving path
+ * @property durationInSec mp4 file auto divided in seconds
+ *
+ * @constructor
+ * @param context context
+ * @param callBack mp4 capture status, see [ICaptureCallBack]
+ *
+ * @author Created by jiangdg on 2022/2/10
+ */
+class Mp4Muxer(
+ context: Context?,
+ callBack: ICaptureCallBack,
+ private var path: String? = null,
+ private val durationInSec: Long = 0
+) {
+ private var mContext: Context? = null
+ private var mMediaMuxer: MediaMuxer? = null
+ private var mFileSubIndex: Int = 0
+ private var mVideoTrackerIndex = -1
+ private var mAudioTrackerIndex = -1
+ private var mVideoFormat: MediaFormat? = null
+ private var mAudioFormat: MediaFormat? = null
+ private var mBeginMillis: Long = 0
+ private var mCaptureCallBack: ICaptureCallBack? = null
+ private var mMainHandler: Handler = Handler(Looper.getMainLooper())
+ private var mOriginalPath: String? = null
+ private var mVideoPts: Long = 0L
+ private var mAudioPts: Long = 0L
+ private val mDateFormat by lazy {
+ SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault())
+ }
+ private val mCameraDir by lazy {
+ "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera"
+ }
+
+ init {
+ this.mCaptureCallBack = callBack
+ this.mContext= context
+ try {
+ if (path.isNullOrEmpty()) {
+ val date = mDateFormat.format(System.currentTimeMillis())
+ path = "$mCameraDir/VID_JJCamera_$date"
+ }
+ mOriginalPath = path
+ path = "${path}.mp4"
+ mMediaMuxer = MediaMuxer(path!!, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
+ } catch (e: Exception) {
+ mCaptureCallBack?.onError(e.localizedMessage)
+ Logger.e(TAG, "init media muxer failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ /**
+ * Add tracker
+ *
+ * @param mediaFormat media format, see [MediaFormat]
+ * @param isVideo media type, audio or video
+ */
+ @Synchronized
+ fun addTracker(mediaFormat: MediaFormat?, isVideo: Boolean) {
+ if (isMuxerStarter() || mediaFormat == null) {
+ return
+ }
+ try {
+ mMediaMuxer?.apply {
+ val tracker = addTrack(mediaFormat)
+ if (isVideo) {
+ mVideoFormat = mediaFormat
+ mVideoTrackerIndex = tracker
+ if (mAudioTrackerIndex != -1) {
+ start()
+ mMainHandler.post {
+ mCaptureCallBack?.onBegin()
+ }
+ mBeginMillis = System.currentTimeMillis()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "start media muxer")
+ }
+ }
+ } else {
+ mAudioFormat = mediaFormat
+ mAudioTrackerIndex = tracker
+ if (mVideoTrackerIndex != -1) {
+ start()
+ mMainHandler.post {
+ mCaptureCallBack?.onBegin()
+ }
+ mBeginMillis = System.currentTimeMillis()
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "start media muxer")
+ }
+ }
+ }
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "addTracker index = $tracker isVideo = $isVideo")
+ }
+ }
+ } catch (e: Exception) {
+ release()
+ mMainHandler.post {
+ mCaptureCallBack?.onError(e.localizedMessage)
+ }
+ Logger.e(TAG, "addTracker failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ /**
+ * write audio(aac) or video(h264) data to media muxer
+ *
+ * @param outputBuffer encode output buffer, see [MediaCodec]
+ * @param bufferInfo encode output buffer info, see [MediaCodec.BufferInfo]
+ * @param isVideo media data type, audio or video
+ */
+ @Synchronized
+ fun pumpStream(outputBuffer: ByteBuffer, bufferInfo: MediaCodec.BufferInfo, isVideo: Boolean) {
+ try {
+ if (!isMuxerStarter()) {
+ return
+ }
+ if (bufferInfo.size <= 0) {
+ return
+ }
+ val index = if (isVideo) {
+ if (mVideoPts == 0L) {
+ mVideoPts = bufferInfo.presentationTimeUs
+ }
+ bufferInfo.presentationTimeUs = bufferInfo.presentationTimeUs - mVideoPts
+ mVideoTrackerIndex
+ } else {
+ if (mAudioPts == 0L) {
+ mAudioPts = bufferInfo.presentationTimeUs
+ }
+ bufferInfo.presentationTimeUs = bufferInfo.presentationTimeUs - mAudioPts
+ mAudioTrackerIndex
+ }
+ outputBuffer.position(bufferInfo.offset)
+ outputBuffer.limit(bufferInfo.offset + bufferInfo.size)
+ mMediaMuxer?.writeSampleData(index, outputBuffer, bufferInfo)
+ saveNewFileIfNeed()
+ } catch (e: Exception) {
+ Logger.e(TAG, "pumpStream failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ private fun saveNewFileIfNeed() {
+ try {
+ val endMillis = System.currentTimeMillis()
+ if (durationInSec == 0L) {
+ return
+ }
+ if (endMillis - mBeginMillis <= durationInSec * 1000) {
+ return
+ }
+
+ mMediaMuxer?.stop()
+ mMediaMuxer?.release()
+ mMediaMuxer = null
+ mAudioTrackerIndex = -1
+ mVideoTrackerIndex = -1
+ mAudioPts = 0L
+ mVideoPts = 0L
+ insertDCIM(mContext, path)
+
+ path = "${mOriginalPath}_${++mFileSubIndex}.mp4"
+ mMediaMuxer = MediaMuxer(path!!, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
+ addTracker(mVideoFormat, true)
+ addTracker(mAudioFormat, false)
+ } catch (e: Exception) {
+ mMainHandler.post {
+ mCaptureCallBack?.onError(e.localizedMessage)
+ }
+ Logger.e(TAG, "release media muxer failed, err = ${e.localizedMessage}", e)
+ }
+ }
+
+ /**
+ * Release mp4 muxer resource
+ */
+ @Synchronized
+ fun release() {
+ try {
+ mMediaMuxer?.stop()
+ mMediaMuxer?.release()
+ insertDCIM(mContext, path, true)
+ } catch (e: Exception) {
+ mMainHandler.post {
+ mCaptureCallBack?.onError(e.localizedMessage)
+ }
+ Logger.e(TAG, "release media muxer failed, err = ${e.localizedMessage}", e)
+ } finally {
+ mMediaMuxer = null
+ mAudioTrackerIndex = -1
+ mVideoTrackerIndex = -1
+ mAudioPts = 0L
+ mVideoPts = 0L
+ }
+ }
+
+ fun getSavePath() = path
+
+ private fun insertDCIM(context: Context?, videoPath: String?, notifyOut: Boolean = false) {
+ context?.let { ctx ->
+ if (videoPath.isNullOrEmpty()) {
+ return
+ }
+ ctx.contentResolver.let { content ->
+ val uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ content.insert(uri, getVideoContentValues(videoPath))
+ mMainHandler.post {
+ mCaptureCallBack?.onComplete(this.path)
+ }
+ }
+ }
+ }
+
+ private fun getVideoContentValues(path: String): ContentValues {
+ val file = File(path)
+ val values = ContentValues()
+ values.put(MediaStore.Video.Media.DATA, path)
+ values.put(MediaStore.Video.Media.DISPLAY_NAME, file.name)
+ values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
+ values.put(MediaStore.Video.Media.SIZE, file.totalSpace)
+ if (MediaUtils.isAboveQ()) {
+ val relativePath = "${Environment.DIRECTORY_DCIM}${File.separator}Camera"
+ val dateExpires = (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000
+ values.put(MediaStore.Video.Media.RELATIVE_PATH, relativePath)
+ values.put(MediaStore.Video.Media.DATE_EXPIRES, dateExpires)
+ }
+ return values
+ }
+
+
+ private fun isMuxerStarter() = mVideoTrackerIndex != -1 && mAudioTrackerIndex != -1
+
+ companion object {
+ private const val TAG = "Mp4Muxer"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/RenderManager.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/RenderManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2eef1053cf4f14d6d6b8038810998f37bc886f90
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/RenderManager.kt
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render
+
+import android.Manifest
+import android.content.ContentValues
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.SurfaceTexture
+import android.location.Location
+import android.location.LocationManager
+import android.opengl.EGLContext
+import android.os.*
+import android.provider.MediaStore
+import android.view.Surface
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import com.jiangdg.ausbc.callback.ICaptureCallBack
+import com.jiangdg.ausbc.render.env.RotateType
+import com.jiangdg.ausbc.render.effect.AbstractEffect
+import com.jiangdg.ausbc.render.internal.*
+import com.jiangdg.ausbc.utils.*
+import com.jiangdg.ausbc.utils.bus.BusKey
+import com.jiangdg.ausbc.utils.bus.EventBus
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * Render manager
+ *
+ * @property previewWidth camera preview width
+ * @property previewHeight camera preview height
+ *
+ * @param context context
+ *
+ * @author Created by jiangdg on 2021/12/28
+ */
+class RenderManager(context: Context, private val previewWidth: Int, private val previewHeight: Int) :
+ SurfaceTexture.OnFrameAvailableListener, Handler.Callback {
+ private var mTextureId: Int = 0
+ private var mRenderThread: HandlerThread? = null
+ private var mRenderHandler: Handler? = null
+ private var mRenderCodecThread: HandlerThread? = null
+ private var mRenderCodecHandler: Handler? = null
+ private var mCameraRender: CameraRender? = null
+ private var mScreenRender: ScreenRender? = null
+ private var mEncodeRender: EncodeRender? = null
+ private var mCaptureRender: CaptureRender? = null
+ private var mCameraSurfaceTexture: SurfaceTexture? = null
+ private var mTransformMatrix: FloatArray = FloatArray(16)
+ private var mWidth: Int = 0
+ private var mHeight: Int = 0
+ private var mFBOId: Int = 0
+ private var mContext: Context = context
+ private var mEffectList = arrayListOf()
+ private var mCacheEffectList = arrayListOf()
+ private var mCaptureDataCb: ICaptureCallBack? = null
+ private var mFrameRate = 0
+ private var mEndTime: Long = 0L
+ private var mStartTime = System.currentTimeMillis()
+ private val mMainHandler: Handler by lazy {
+ Handler(Looper.getMainLooper())
+ }
+ private val mCaptureState: AtomicBoolean by lazy {
+ AtomicBoolean(false)
+ }
+ private val mDateFormat by lazy {
+ SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault())
+ }
+ private val mCameraDir by lazy {
+ "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera"
+ }
+
+ init {
+ this.mCameraRender = CameraRender(context)
+ this.mScreenRender = ScreenRender(context)
+ this.mCaptureRender = CaptureRender(context)
+ addLifecycleObserver(context)
+
+ Logger.i(TAG, "create RenderManager, Open ES version is ${Utils.getGLESVersion(context)}")
+ }
+
+ /**
+ * Rendering processing logic
+ *
+ * Note: EGL must be initialized first, otherwise GL cannot run
+ */
+ override fun handleMessage(msg: Message): Boolean {
+ Utils.getGLESVersion(mContext)?.let { version ->
+ if (version.toFloat() < 2F) {
+ Logger.e(TAG, "OpenGL ES version(${version}) is too lower")
+ return true
+ }
+ }
+ when (msg.what) {
+ MSG_GL_INIT -> {
+ (msg.obj as Triple<*, *, *>).apply {
+ val w = first as Int
+ val h = second as Int
+ val surface = third as? Surface
+ mScreenRender?.initEGLEvn()
+ mScreenRender?.setupSurface(surface, w, h)
+ mScreenRender?.eglMakeCurrent()
+ mCameraRender?.initGLES()
+ mScreenRender?.initGLES()
+ mCaptureRender?.initGLES()
+ EventBus.with(BusKey.KEY_RENDER_READY).postMessage(true)
+ }
+ }
+ MSG_GL_CHANGED_SIZE -> {
+ (msg.obj as Pair<*, *>).apply {
+ mWidth = first as Int
+ mHeight = second as Int
+ mCameraRender?.setSize(mWidth, mHeight)
+ mScreenRender?.setSize(mWidth, mHeight)
+ mCaptureRender?.setSize(mWidth, mHeight)
+ mCameraSurfaceTexture?.setDefaultBufferSize(mWidth, mHeight)
+ }
+ }
+ MSG_GL_SAVE_IMAGE -> {
+ saveImageInternal(msg.obj as? String)
+ }
+ MSG_GL_START_RENDER_CODEC -> {
+ (msg.obj as Triple<*, *, *>).apply {
+ val surface = first as Surface
+ val width = second as Int
+ val height = third as Int
+ startRenderCodecInternal(surface, width, height)
+ }
+ }
+ MSG_GL_STOP_RENDER_CODEC -> {
+ stopRenderCodecInternal()
+ }
+ MSG_GL_ROUTE_ANGLE -> {
+ (msg.obj as? RotateType)?.apply {
+ mCameraRender?.setRotateAngle(this)
+ }
+ }
+ MSG_GL_DRAW -> {
+ // 将摄像头数据渲染到SurfaceTexture
+ // 同时设置图像的矫正矩阵
+ mCameraSurfaceTexture?.updateTexImage()
+ mCameraSurfaceTexture?.getTransformMatrix(mTransformMatrix)
+ mCameraRender?.setTransformMatrix(mTransformMatrix)
+ mCameraRender?.drawFrame(mTextureId)
+ // 滤镜、渲染处理
+ mCameraRender?.getFboTextureId()?.let { fboId ->
+ var effectId = fboId
+ mEffectList.forEach { effectRender ->
+ effectRender.drawFrame(effectId)
+ effectId = effectRender.getFboTextureId()
+ }
+ effectId
+ }?.also { id ->
+ mScreenRender?.drawFrame(id)
+ drawFrame2Capture(id)
+ drawFrame2Codec(id, mCameraSurfaceTexture?.timestamp ?: 0)
+ mScreenRender?.swapBuffers(mCameraSurfaceTexture?.timestamp ?: 0)
+ }
+ }
+ MSG_GL_ADD_EFFECT -> {
+ (msg.obj as? AbstractEffect)?.let { effect->
+ if (mEffectList.contains(effect)) {
+ return@let
+ }
+ effect.initGLES()
+ effect.setSize(mWidth, mHeight)
+ mEffectList.add(effect)
+ mCacheEffectList.add(effect)
+ Logger.i(TAG, "add effect, name = ${effect.javaClass.simpleName}, size = ${mEffectList.size}")
+ }
+ }
+ MSG_GL_REMOVE_EFFECT -> {
+ (msg.obj as? AbstractEffect)?.let {
+ if (! mEffectList.contains(it)) {
+ return@let
+ }
+ it.releaseGLES()
+ mEffectList.remove(it)
+ mCacheEffectList.remove(it)
+ Logger.i(TAG, "remove effect, name = ${it.javaClass.simpleName}, size = ${mEffectList.size}")
+ }
+ }
+ MSG_GL_RELEASE -> {
+ EventBus.with(BusKey.KEY_RENDER_READY).postMessage(false)
+ mEffectList.forEach { effect ->
+ effect.releaseGLES()
+ }
+ mEffectList.clear()
+ mCameraRender?.releaseGLES()
+ mScreenRender?.releaseGLES()
+ mCaptureRender?.releaseGLES()
+ mCameraSurfaceTexture?.setOnFrameAvailableListener(null)
+ mCameraSurfaceTexture = null
+ }
+ }
+ return true
+ }
+
+ private fun drawFrame2Capture(fboId: Int) {
+ mCaptureRender?.drawFrame(fboId)
+ mCaptureRender?.getFboTextureId()?.let { id->
+ mFBOId = id
+ }
+ }
+
+ /**
+ * Start render screen
+ *
+ * @param w surface width
+ * @param h surface height
+ * @param outSurface render surface
+ * @param listener acquire camera surface texture, see [CameraSurfaceTextureListener]
+ */
+ fun startRenderScreen(w: Int, h: Int, outSurface: Surface?, listener: CameraSurfaceTextureListener? = null) {
+ if (mCameraSurfaceTexture == null) {
+ mTextureId = mCameraRender?.createOESTexture()!!
+ mCameraSurfaceTexture = SurfaceTexture(mTextureId)
+ mCameraSurfaceTexture?.setOnFrameAvailableListener(this)
+ }
+ listener?.onSurfaceTextureAvailable(mCameraSurfaceTexture)
+ mRenderThread = HandlerThread(RENDER_THREAD)
+ mRenderThread?.start()
+ mRenderHandler = Handler(mRenderThread!!.looper, this@RenderManager)
+ Triple(w, h, outSurface).apply {
+ mRenderHandler?.obtainMessage(MSG_GL_INIT, this)?.sendToTarget()
+ }
+ setRenderSize(w, h)
+ }
+
+ /**
+ * Stop render screen
+ */
+ fun stopRenderScreen() {
+ mRenderHandler?.obtainMessage(MSG_GL_RELEASE)?.sendToTarget()
+ mRenderThread?.quitSafely()
+ mRenderThread = null
+ mRenderHandler = null
+ }
+
+ /**
+ * Start render codec
+ *
+ * @param inputSurface mediacodec input surface, see [android.media.MediaCodec]
+ * @param width camera preview width
+ * @param height camera preview height
+ */
+ fun startRenderCodec(inputSurface: Surface, width: Int, height: Int) {
+ Triple(inputSurface, width, height).apply {
+ mRenderHandler?.obtainMessage(MSG_GL_START_RENDER_CODEC, this)?.sendToTarget()
+ }
+ }
+
+ /**
+ * Stop render codec
+ */
+ fun stopRenderCodec() {
+ mRenderHandler?.obtainMessage(MSG_GL_STOP_RENDER_CODEC)?.sendToTarget()
+ }
+
+ /**
+ * Set render size
+ *
+ * @param w surface width
+ * @param h surface height
+ */
+ fun setRenderSize(w: Int, h: Int) {
+ mRenderHandler?.obtainMessage(MSG_GL_CHANGED_SIZE, Pair(w, h))?.sendToTarget()
+ }
+
+ /**
+ * Add render effect
+ *
+ * @param effect add a effect, see [AbstractEffect]
+ */
+ fun addRenderEffect(effect: AbstractEffect?) {
+ mRenderHandler?.obtainMessage(MSG_GL_ADD_EFFECT, effect)?.sendToTarget()
+ }
+
+ /**
+ * Remove render effect
+ *
+ * @param effect a effect removed, see [AbstractEffect]
+ */
+ fun removeRenderEffect(effect: AbstractEffect?) {
+ mRenderHandler?.obtainMessage(MSG_GL_REMOVE_EFFECT, effect)?.sendToTarget()
+ }
+
+ /**
+ * Rotate camera render angle
+ *
+ * @param type rotate angle, null means rotating nothing
+ * see [RotateType.ANGLE_90], [RotateType.ANGLE_270],...etc.
+ */
+ fun setRotateType(type: RotateType?) {
+ mRenderHandler?.obtainMessage(MSG_GL_ROUTE_ANGLE, type)?.sendToTarget()
+ }
+
+ /**
+ * Get cache render effect list
+ * @return current effects
+ */
+ fun getCacheEffectList() = mCacheEffectList
+
+ /**
+ * Save image
+ *
+ * @param callBack capture image status, see [ICaptureCallBack]
+ * @param path custom image path
+ */
+ fun saveImage(callBack: ICaptureCallBack?, path: String?) {
+ this.mCaptureDataCb = callBack
+ mRenderHandler?.obtainMessage(MSG_GL_SAVE_IMAGE, path)?.sendToTarget()
+ }
+
+ override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
+ emitFrameRate()
+ mRenderHandler?.obtainMessage(MSG_GL_DRAW)?.sendToTarget()
+ }
+
+ private fun startRenderCodecInternal(surface: Surface, w: Int, h: Int) {
+ stopRenderCodecInternal()
+ mRenderCodecThread = HandlerThread(RENDER_CODEC_THREAD)
+ mRenderCodecThread?.start()
+ mRenderCodecHandler = Handler(mRenderCodecThread!!.looper) { message ->
+ when (message.what) {
+ MSG_GL_RENDER_CODEC_INIT -> {
+ (message.obj as Pair<*, *>).apply {
+ val shareContext = first as EGLContext
+ val inputSurface = second as Surface
+ mEncodeRender = EncodeRender(mContext)
+ mEncodeRender?.initEGLEvn(shareContext)
+ mEncodeRender?.setupSurface(inputSurface)
+ mEncodeRender?.initGLES()
+ }
+ }
+ MSG_GL_RENDER_CODEC_CHANGED_SIZE -> {
+ (message.obj as Pair<*, *>).apply {
+ val width = first as Int
+ val height = second as Int
+ mEncodeRender?.setSize(width, height)
+ }
+ }
+ MSG_GL_RENDER_CODEC_DRAW -> {
+ (message.obj as Pair<*, *>).apply {
+ val textureId = first as Int
+ val timeStamps = second as Long
+ mEncodeRender?.drawFrame(textureId)
+ mEncodeRender?.swapBuffers(timeStamps)
+ }
+ }
+ MSG_GL_RENDER_CODEC_RELEASE -> {
+ mEncodeRender?.releaseGLES()
+ mEncodeRender = null
+ }
+ }
+ true
+ }
+ mScreenRender?.getCurrentContext().let {
+ if (it == null) {
+ throw NullPointerException("Current EGLContext can't be null.")
+ }
+ mRenderCodecHandler?.obtainMessage(MSG_GL_RENDER_CODEC_INIT, Pair(it, surface))?.sendToTarget()
+ }
+ mRenderCodecHandler?.obtainMessage(MSG_GL_RENDER_CODEC_CHANGED_SIZE, Pair(w, h))?.sendToTarget()
+ }
+
+ private fun drawFrame2Codec(textureId: Int, timeStamps: Long) {
+ Pair(textureId, timeStamps).apply {
+ mRenderCodecHandler?.obtainMessage(MSG_GL_RENDER_CODEC_DRAW, this)?.sendToTarget()
+ }
+ }
+
+ private fun stopRenderCodecInternal() {
+ mRenderCodecHandler?.obtainMessage(MSG_GL_RENDER_CODEC_RELEASE)?.sendToTarget()
+ mRenderCodecThread?.quitSafely()
+ mRenderCodecThread = null
+ mRenderCodecHandler = null
+ }
+
+ private fun saveImageInternal(savePath: String?) {
+ if (mCaptureState.get()) {
+ return
+ }
+ mCaptureState.set(true)
+ mMainHandler.post {
+ mCaptureDataCb?.onBegin()
+ }
+ val date = mDateFormat.format(System.currentTimeMillis())
+ val title = savePath ?: "IMG_JJCamera_$date"
+ val displayName = savePath ?: "$title.jpg"
+ val path = savePath ?: "$mCameraDir/$displayName"
+ val width = mWidth
+ val height = mHeight
+ val location = getGpsLocation()
+ // 写入文件
+ // glReadPixels读取的是大端数据,但是我们保存的是小端
+ // 故需要将图片上下颠倒为正
+ var fos: FileOutputStream? = null
+ try {
+ fos = FileOutputStream(path)
+ GLBitmapUtils.transFrameBufferToBitmap(mFBOId, width, height).apply {
+ compress(Bitmap.CompressFormat.JPEG, 100, fos)
+ recycle()
+ }
+ } catch (e: IOException) {
+ mMainHandler.post {
+ mCaptureDataCb?.onError(e.localizedMessage)
+ }
+ Logger.e(TAG, "Failed to write file, err = ${e.localizedMessage}", e)
+ } finally {
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ Logger.e(TAG, "Failed to write file, err = ${e.localizedMessage}", e)
+ }
+ }
+ // 判断是否保存成功
+ // 如果成功,则更新图库
+ val file = File(path)
+ if (file.length() == 0L) {
+ Logger.e(TAG, "Failed to save file $path")
+ file.delete()
+ mCaptureState.set(false)
+ return
+ }
+ val values = ContentValues()
+ values.put(MediaStore.Images.ImageColumns.TITLE, title)
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
+ values.put(MediaStore.Images.ImageColumns.DATA, path)
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
+ values.put(MediaStore.Images.ImageColumns.WIDTH, width)
+ values.put(MediaStore.Images.ImageColumns.HEIGHT, height)
+ values.put(MediaStore.Images.ImageColumns.LONGITUDE, location?.longitude)
+ values.put(MediaStore.Images.ImageColumns.LATITUDE, location?.latitude)
+ mContext.contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
+ mMainHandler.post {
+ mCaptureDataCb?.onComplete(path)
+ }
+ mCaptureState.set(false)
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "captureImageInternal save path = $path")
+ }
+ }
+
+ private fun addLifecycleObserver(context: Context) {
+ if (context !is LifecycleOwner) return
+ context.lifecycle.addObserver(object : LifecycleEventObserver {
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ when (event) {
+ Lifecycle.Event.ON_DESTROY -> {
+ stopRenderScreen()
+ }
+ else -> {}
+ }
+ }
+ })
+ }
+
+ private fun getGpsLocation(): Location? {
+ mContext.let { ctx->
+ val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ val locPermission = ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION)
+ if (locPermission == PackageManager.PERMISSION_GRANTED) {
+ return locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
+ }
+ }
+ return null
+ }
+
+ private fun emitFrameRate() {
+ mFrameRate++
+ mEndTime = System.currentTimeMillis()
+ if (mEndTime - mStartTime >= 1000) {
+ if (Utils.debugCamera) {
+ Logger.i(TAG, "camera render frame rate is $mFrameRate fps")
+ }
+ EventBus.with(BusKey.KEY_FRAME_RATE).postMessage(mFrameRate)
+ mStartTime = mEndTime
+ mFrameRate = 0
+ }
+ }
+
+ /**
+ * Camera surface texture listener
+ *
+ * @constructor Create empty Camera surface texture listener
+ */
+ interface CameraSurfaceTextureListener {
+ /**
+ * On surface texture available
+ *
+ * @param surfaceTexture camera render surface texture
+ */
+ fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture?)
+ }
+
+ companion object {
+ private const val TAG = "RenderManager"
+ private const val RENDER_THREAD = "gl_render"
+ private const val RENDER_CODEC_THREAD = "gl_render_codec"
+ // render
+ private const val MSG_GL_INIT = 0x00
+ private const val MSG_GL_DRAW = 0x01
+ private const val MSG_GL_RELEASE = 0x02
+ private const val MSG_GL_START_RENDER_CODEC = 0x03
+ private const val MSG_GL_STOP_RENDER_CODEC = 0x04
+ private const val MSG_GL_CHANGED_SIZE = 0x05
+ private const val MSG_GL_ADD_EFFECT = 0x06
+ private const val MSG_GL_REMOVE_EFFECT = 0x07
+ private const val MSG_GL_SAVE_IMAGE = 0x08
+ private const val MSG_GL_ROUTE_ANGLE = 0x09
+
+ // codec
+ private const val MSG_GL_RENDER_CODEC_INIT = 0x11
+ private const val MSG_GL_RENDER_CODEC_CHANGED_SIZE = 0x12
+ private const val MSG_GL_RENDER_CODEC_DRAW = 0x13
+ private const val MSG_GL_RENDER_CODEC_RELEASE = 0x14
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/AbstractEffect.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/AbstractEffect.kt
new file mode 100644
index 0000000000000000000000000000000000000000..36679abda749c13333de974f81eb561c1bac00ba
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/AbstractEffect.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.effect
+
+import android.content.Context
+import com.jiangdg.ausbc.render.internal.AbstractFboRender
+
+/** abstract effect class, extended from AbstractFboRender
+ *
+ * @author Created by jiangdg on 2022/1/26
+ */
+abstract class AbstractEffect(ctx: Context) : AbstractFboRender(ctx) {
+
+ /**
+ * Get effect id
+ *
+ * @return effect id
+ */
+ abstract fun getId(): Int
+
+ /**
+ * Get classify id
+ *
+ * @return effect classify id
+ */
+ abstract fun getClassifyId(): Int
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectBlackWhite.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectBlackWhite.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0b0ede0683bdf95971bb3eb0166967ec3ff1978a
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectBlackWhite.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.effect
+
+import android.content.Context
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.effect.bean.CameraEffect
+
+/** Black White effect
+ *
+ * @author Created by jiangdg on 2022/1/26
+ */
+class EffectBlackWhite(ctx: Context) : AbstractEffect(ctx) {
+
+ override fun getId(): Int = ID
+
+ override fun getClassifyId(): Int = CameraEffect.CLASSIFY_ID_FILTER
+
+ override fun getVertexSourceId(): Int = R.raw.base_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.effect_blackw_fragment
+
+ companion object {
+ const val ID = 100
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectSoul.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectSoul.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a78d5841931f6578ba513f552a5923e7e60e0def
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectSoul.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.effect
+
+import android.content.Context
+import android.opengl.GLES20
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.effect.bean.CameraEffect
+
+/** Soul effect
+ *
+ * @author Created by jiangdg on 2022/2/17
+ */
+class EffectSoul(context: Context): AbstractEffect(context) {
+
+ private var mTimeStampsHandler = -1
+ private var mTimeCount = 0
+
+ override fun getId(): Int = ID
+
+ override fun getClassifyId(): Int = CameraEffect.CLASSIFY_ID_ANIMATION
+
+ override fun init() {
+ mTimeStampsHandler = GLES20.glGetUniformLocation(mProgram, "timeStamps")
+ }
+
+ override fun beforeDraw() {
+ if (mTimeCount > 65535) {
+ mTimeCount = 0
+ }
+ GLES20.glUniform1f(mTimeStampsHandler, (++mTimeCount % 9).toFloat())
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.base_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.effect_soul_fragment
+
+ companion object {
+ const val ID = 200
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectZoom.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectZoom.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d8f644dd867ac66c39a97ee21cddc9d2b62c068c
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/EffectZoom.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.effect
+
+import android.content.Context
+import android.opengl.GLES20
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.effect.bean.CameraEffect
+
+/** Zoom effect
+ *
+ * @author Created by jiangdg on 2022/01/28
+ */
+class EffectZoom(context: Context) : AbstractEffect(context) {
+
+ private var mTimeStampsHandler = -1
+ private var mTimeCount = 0
+
+ override fun getId(): Int = ID
+
+ override fun getClassifyId(): Int = CameraEffect.CLASSIFY_ID_ANIMATION
+
+ override fun init() {
+ mTimeStampsHandler = GLES20.glGetUniformLocation(mProgram, "timeStamps")
+ }
+
+ override fun beforeDraw() {
+ if (mTimeCount > 65535) {
+ mTimeCount = 0
+ }
+ GLES20.glUniform1f(mTimeStampsHandler, (++mTimeCount % 9).toFloat())
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.effect_zoom_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.base_fragment
+
+ companion object {
+ const val ID = 300
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/bean/CameraEffect.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/bean/CameraEffect.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e61f9d2f1ceefcc1cd4e6c9dc830c3a6a2393299
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/effect/bean/CameraEffect.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.effect.bean
+
+import androidx.annotation.Keep
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.effect.AbstractEffect
+
+/** camera filter info
+ *
+ * @author Created by jiangdg on 2022/3/16
+ */
+@Keep
+data class CameraEffect(
+ val id: Int,
+ val name: String,
+ val classifyId: Int,
+ val effect: AbstractEffect? = null,
+ val coverResId: Int? = null,
+ val coverUrl: String? = null
+) {
+ companion object {
+ val NONE_FILTER by lazy {
+ CameraEffect(
+ ID_NONE_FILTER,
+ NAME_NONE,
+ CLASSIFY_ID_FILTER,
+ coverResId = R.drawable.effect_none
+ )
+ }
+
+ val NONE_ANIMATION by lazy {
+ CameraEffect(
+ ID_NONE_ANIMATION,
+ NAME_NONE,
+ CLASSIFY_ID_ANIMATION,
+ coverResId = R.drawable.effect_none
+ )
+ }
+
+ const val CLASSIFY_ID_FILTER = 1
+ const val CLASSIFY_ID_ANIMATION = 2
+ const val ID_NONE_FILTER = -1
+ const val ID_NONE_ANIMATION = -2
+ private const val NAME_NONE = "None"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/env/EGLEvn.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/env/EGLEvn.kt
new file mode 100644
index 0000000000000000000000000000000000000000..362fdaaebba882aa69c069c12bc2c688baff9013
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/env/EGLEvn.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.env
+
+import android.opengl.*
+import android.opengl.EGLSurface
+import android.view.Surface
+import com.jiangdg.ausbc.utils.Logger
+
+/**
+ * 创建EGL,将其与目标Surface绑定
+ *
+ * @author Created by jiangdg on 2021/10/14
+ */
+class EGLEvn {
+ private var mEglDisplay: EGLDisplay = EGL14.EGL_NO_DISPLAY
+ private var mEglSurface: EGLSurface = EGL14.EGL_NO_SURFACE
+ private var mEglContext: EGLContext = EGL14.EGL_NO_CONTEXT
+ private var mSurface: Surface? = null
+ private val configs = arrayOfNulls(1)
+
+ fun initEgl(curContext: EGLContext? = null): Boolean {
+ // 1. 获取EGL Display
+ mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
+ if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
+ loggerError("Get display")
+ return false
+ }
+ // 2. 初始化EGL
+ val version = IntArray(2)
+ if (! EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
+ loggerError("Init egl")
+ return false
+ }
+ // 3. 指定Surface配置
+ // RGB888 & opengl ES2
+ // EGL_RECORDABLE_ANDROID(API26以下必须指定)
+ val configAttribs = intArrayOf(
+ EGL14.EGL_RED_SIZE, 8,
+ EGL14.EGL_GREEN_SIZE, 8,
+ EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_ALPHA_SIZE, 8,
+ EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
+ EGL_RECORDABLE_ANDROID, 1,
+ EGL14.EGL_NONE
+ )
+ val numConfigs = IntArray(1)
+ if (! EGL14.eglChooseConfig(mEglDisplay, configAttribs, 0, configs, 0, configs.size, numConfigs, 0)) {
+ loggerError("Choose Config")
+ return false
+ }
+ // 4. 创建OpenGL ES对应的上下文
+ // 如果传入了glContext,则使用传入的上下文,即纹理共享
+ val ctxAttribs = intArrayOf(
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL14.EGL_NONE
+ )
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, configs[0], curContext ?: EGL14.EGL_NO_CONTEXT , ctxAttribs, 0)
+ if (mEglContext == EGL14.EGL_NO_CONTEXT) {
+ loggerError("Create context")
+ return false
+ }
+ // 5. 设置默认的上下文环境和输出缓冲区
+ // 将eglSurface先设置为EGL14.EGL_NO_SURFACE
+ if (! EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, mEglContext)) {
+ loggerError("Bind context and window")
+ return false
+ }
+ Logger.i(TAG, "Init EGL Success!")
+ return true
+ }
+
+ fun setupSurface(surface: Surface?, surfaceWidth: Int = 0, surfaceHeight: Int = 0) {
+ if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
+ return
+ }
+ mEglSurface = if (surface == null) {
+ val attributes = intArrayOf(
+ EGL14.EGL_WIDTH, surfaceWidth,
+ EGL14.EGL_HEIGHT, surfaceHeight,
+ EGL14.EGL_NONE
+ )
+ EGL14.eglCreatePbufferSurface(mEglDisplay, configs[0], attributes , 0)
+ } else {
+ val attributes = intArrayOf(
+ EGL14.EGL_NONE
+ )
+ EGL14.eglCreateWindowSurface(mEglDisplay, configs[0], surface, attributes , 0)
+ }
+ if (mEglSurface == EGL14.EGL_NO_SURFACE) {
+ loggerError("Create window")
+ }
+ mSurface = surface
+ Logger.i(TAG, "setupSurface Success!")
+ }
+
+ fun eglMakeCurrent() {
+ if (mEglContext == EGL14.EGL_NO_CONTEXT) {
+ return
+ }
+ if (mEglSurface == EGL14.EGL_NO_SURFACE) {
+ return
+ }
+ if (! EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ loggerError("Bind context and window")
+ }
+ }
+
+ fun setPresentationTime(nanoseconds: Long) {
+ if (mEglContext == EGL14.EGL_NO_CONTEXT) {
+ return
+ }
+ if (mSurface == null) {
+ return
+ }
+ // 更新EGL显示时间戳
+ if (! EGLExt.eglPresentationTimeANDROID(mEglDisplay, mEglSurface, nanoseconds)) {
+ loggerError("Set Presentation time")
+ }
+ }
+
+ fun swapBuffers() {
+ if (mEglContext == EGL14.EGL_NO_CONTEXT) {
+ return
+ }
+ // 交换双重缓冲数据
+ // 即将渲染数据(后端缓冲区)输出到目标窗口(Surface)(前端缓冲区)
+ if (! EGL14.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ loggerError("Swap buffers")
+ }
+ }
+
+ fun releaseElg() {
+ if (mEglDisplay != EGL14.EGL_NO_DISPLAY) {
+ EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)
+ EGL14.eglDestroySurface(mEglDisplay, mEglSurface)
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext)
+ EGL14.eglReleaseThread()
+ EGL14.eglTerminate(mEglDisplay)
+ }
+ mSurface?.release()
+ mEglDisplay = EGL14.EGL_NO_DISPLAY
+ mEglSurface = EGL14.EGL_NO_SURFACE
+ mEglContext = EGL14.EGL_NO_CONTEXT
+ mSurface = null
+ Logger.i(TAG, "Release EGL Success!")
+ }
+
+ private fun loggerError(msg: String) {
+ Logger.e(TAG, "$msg failed. error = ${EGL14.eglGetError()}")
+ }
+
+ fun getEGLContext(): EGLContext = EGL14.eglGetCurrentContext()
+
+ companion object {
+ private const val TAG = "EGLEvn"
+ private const val EGL_RECORDABLE_ANDROID = 0x3142
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/env/RotateType.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/env/RotateType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1a66a28789246fc8ebb32020c76386d316984fdd
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/env/RotateType.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.env
+
+/** rotate angle type
+ *
+ * @author Created by jiangdg on 2021/12/28
+ */
+enum class RotateType {
+ ANGLE_0, // default, do nothing
+ ANGLE_90,
+ ANGLE_180,
+ ANGLE_270,
+ FLIP_UP_DOWN, // flip vertically
+ FLIP_LEFT_RIGHT // horizontal flip(mirror)
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractFboRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractFboRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..65b99d180c81ee74f1ecec8bd81b17d739df5b49
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractFboRender.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.opengl.GLES20
+import com.jiangdg.ausbc.utils.Logger
+
+/** A AbstractRender subclass, also abstract
+ * create a fbo,and draw to it instead of screen.
+ *
+ * Attention: Your should set your context as the current context before creating fbo,
+ * Otherwise GLES20.glCheckFramebufferStatus=0 on some other devices!
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+abstract class AbstractFboRender(context: Context) : AbstractRender(context) {
+ private var mFBOTextureId: Int = -1
+
+ fun getFboTextureId() = mFBOTextureId
+
+ override fun drawFrame(textureId: Int) {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFBOTextureId)
+ super.drawFrame(textureId)
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
+ }
+
+ override fun setSize(width: Int, height: Int) {
+ super.setSize(width, height)
+ mFBOTextureId = createFBO(width, height)
+ }
+
+ private fun createFBO(width: Int, height: Int): Int {
+ val fboBuffers = IntArray(1)
+ // 1. 创建、绑定FBO
+ GLES20.glGenFramebuffers(1, fboBuffers, 0)
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboBuffers[0])
+
+ // 2. 创建FBO(普通)纹理,将其绑定到FBO
+ val fboId = createTexture()
+ GLES20.glFramebufferTexture2D(
+ GLES20.GL_FRAMEBUFFER,
+ GLES20.GL_COLOR_ATTACHMENT0,
+ GLES20.GL_TEXTURE_2D,
+ fboId,
+ 0
+ )
+
+ // 3. 设置FBO分配内存的大小
+ GLES20.glTexImage2D(
+ GLES20.GL_TEXTURE_2D, 0,
+ GLES20.GL_RGBA, width, height, 0,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null
+ )
+ val status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)
+ if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
+ Logger.e(TAG, "glFramebufferTexture2D err = ${GLES20.glGetError()}")
+ }
+ // 4. 解绑纹理和FBO
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
+ return fboBuffers[0]
+ }
+
+ companion object {
+ private const val TAG = "AbstractFboRender"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0d207e20101ca360573a3bb3a4c7f9cf7120c47a
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/AbstractRender.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.opengl.GLES11Ext
+import android.opengl.GLES20
+import android.util.Log
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.FloatBuffer
+
+/** Abstract render class based on opengl es 2.0
+ * openGL ES initial and frame drawing
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+abstract class AbstractRender(context: Context) {
+ private var mFragmentShader: Int = 0
+ private var mVertexShader: Int = 0
+ protected var mProgram: Int = 0
+ private var mContext: Context? = null
+ private var mWidth: Int = 0
+ private var mHeight: Int = 0
+ private var mPositionLocation = 0
+ private var mTextureCoordLocation = 0
+ private var mStMatrixHandle = 0
+ private var mMVPMatrixHandle = 0
+
+ private var mTriangleVertices: FloatBuffer = ByteBuffer.allocateDirect(
+ mTriangleVerticesData.size * FLOAT_SIZE_BYTES
+ ).order(ByteOrder.nativeOrder()).asFloatBuffer()
+
+ init {
+ this.mContext = context
+ this.mTriangleVertices.put(mTriangleVerticesData).position(0)
+ }
+
+ open fun setSize(width: Int, height: Int) {
+ this.mWidth = width
+ this.mHeight = height
+ GLES20.glViewport(0, 0, mWidth, mHeight)
+ }
+
+ open fun drawFrame(textureId: Int) {
+ GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f)
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
+ // 1. 激活程序,绑定纹理
+ GLES20.glUseProgram(mProgram)
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
+ GLES20.glBindTexture(getBindTextureType(), textureId)
+
+ // 2. 链接顶点属性
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET)
+ GLES20.glVertexAttribPointer(mPositionLocation, 3, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices)
+ GLES20.glEnableVertexAttribArray(mPositionLocation)
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET)
+ GLES20.glVertexAttribPointer(mTextureCoordLocation, 2, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices)
+ GLES20.glEnableVertexAttribArray(mTextureCoordLocation)
+
+ beforeDraw()
+
+ // 3. 绘制
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
+ GLES20.glBindTexture(getBindTextureType(), 0)
+ }
+
+ protected open fun beforeDraw() {}
+ protected open fun init() {}
+ protected open fun clear() {}
+ protected open fun getBindTextureType() = GLES20.GL_TEXTURE_2D
+ protected abstract fun getVertexSourceId(): Int
+ protected abstract fun getFragmentSourceId(): Int
+
+ fun initGLES() {
+ val vertexShaderSource = MediaUtils.readRawTextFile(mContext!!, getVertexSourceId())
+ val fragmentShaderSource = MediaUtils.readRawTextFile(mContext!!, getFragmentSourceId())
+ mProgram = createProgram(vertexShaderSource, fragmentShaderSource)
+ if (mProgram == 0) {
+ Logger.e(TAG, "create program failed, err = ${GLES20.glGetError()}")
+ return
+ }
+ mPositionLocation = GLES20.glGetAttribLocation(mProgram, "aPosition")
+ mTextureCoordLocation = GLES20.glGetAttribLocation(mProgram, "aTextureCoordinate")
+ if (isGLESStatusError()) {
+ Logger.e(TAG, "create external texture failed, err = ${GLES20.glGetError()}")
+ return
+ }
+ init()
+ Logger.i(TAG, "init surface texture render success!")
+ }
+
+ fun releaseGLES() {
+ if (mVertexShader != 0) {
+ GLES20.glDeleteShader(mVertexShader)
+ }
+ if (mFragmentShader != 0) {
+ GLES20.glDeleteShader(mFragmentShader)
+ }
+ if (mProgram != 0) {
+ GLES20.glDeleteProgram(mProgram)
+ }
+ clear()
+ Logger.i(TAG, "release surface texture render success!")
+ }
+
+ private fun loadShader(shaderType: Int, source: String): Int {
+ val shader = GLES20.glCreateShader(shaderType)
+ GLES20.glShaderSource(shader, source)
+ GLES20.glCompileShader(shader)
+ val compiled = IntArray(1)
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0)
+ if (compiled[0] == 0) {
+ Log.e(TAG, "Could not compile shader, info = ${GLES20.glGetShaderInfoLog(shader)}, T = ${Thread.currentThread().name}")
+ GLES20.glDeleteShader(shader)
+ return 0
+ }
+ return shader
+ }
+
+ private fun createProgram(vertexSource: String, fragmentSource: String): Int {
+ // 创建顶点、片段着色器
+ mVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource)
+ if (mVertexShader == 0) {
+ Logger.i(TAG, "vertexSource err = ${GLES20.glGetError()}: \n $vertexSource")
+ return 0
+ }
+ mFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource)
+ if (mFragmentShader == 0) {
+ Logger.i(TAG, "fragmentSource err = ${GLES20.glGetError()}: \n $fragmentSource")
+ return 0
+ }
+ // 创建链接程序,并将着色器依附到程序
+ val program = GLES20.glCreateProgram()
+ GLES20.glAttachShader(program, mVertexShader)
+ GLES20.glAttachShader(program, mFragmentShader)
+ GLES20.glLinkProgram(program)
+ val linkStatus = IntArray(1)
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Logger.e(TAG, "create program failed.")
+ GLES20.glDeleteProgram(program)
+ return 0
+ }
+ return program
+ }
+
+ private fun isGLESStatusError() = GLES20.glGetError() != GLES20.GL_NO_ERROR
+
+ protected fun createTexture(): Int {
+ val textures = IntArray(1)
+ GLES20.glGenTextures(1, textures, 0)
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0])
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST)
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
+ Logger.i(TAG, "create texture, id = ${textures[0]}")
+ return textures[0]
+ }
+
+ fun createOESTexture(): Int {
+ val textures = IntArray(1)
+ GLES20.glGenTextures(1, textures, 0)
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0])
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST)
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
+ Logger.i(TAG, "create external texture, id = ${textures[0]}")
+ return textures[0]
+ }
+
+ companion object {
+ private const val TAG = "AbstractRender"
+ private const val FLOAT_SIZE_BYTES = 4
+ private const val TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES
+ private const val TRIANGLE_VERTICES_DATA_POS_OFFSET = 0
+ private const val TRIANGLE_VERTICES_DATA_UV_OFFSET = 3
+
+ private val mTriangleVerticesData = floatArrayOf(
+ // 坐标 纹理
+ // X, Y, Z, U, V
+ -1.0f, -1.0f, 0f, 0f, 0f,
+ 1.0f, -1.0f, 0f, 1f, 0f,
+ -1.0f, 1.0f, 0f, 0f, 1f,
+ 1.0f, 1.0f, 0f, 1f, 1f,
+ )
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CameraRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CameraRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2fe82d3c0bb31454f5dd54f92d8403d0ceac6d0c
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CameraRender.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.opengl.GLES11Ext
+import android.opengl.GLES20
+import android.opengl.Matrix
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.env.RotateType
+import kotlin.math.cos
+import kotlin.math.sin
+
+/** Inherit from AbstractFboRender
+ * render camera data with camera_vertex.glsl and camera_fragment.glsl
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+class CameraRender(context: Context) : AbstractFboRender(context) {
+ private var mStMatrixHandle: Int = -1
+ private var mMVPMatrixHandle: Int = -1
+ private var mStMatrix = FloatArray(16)
+ private var mMVPMatrix = FloatArray(16)
+
+ override fun init() {
+ setMVPMatrix(0)
+ Matrix.setIdentityM(mStMatrix, 0)
+ mStMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uStMatrix")
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
+ }
+
+ override fun beforeDraw() {
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0)
+ GLES20.glUniformMatrix4fv(mStMatrixHandle, 1, false, mStMatrix, 0)
+ }
+
+ override fun getBindTextureType(): Int {
+ return GLES11Ext.GL_TEXTURE_EXTERNAL_OES
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.camera_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.camera_fragment
+
+ fun setRotateAngle(type: RotateType) {
+ val angle = when (type) {
+ RotateType.ANGLE_90 -> 90
+ RotateType.ANGLE_180 -> 180
+ RotateType.ANGLE_270 -> 270
+ RotateType.FLIP_UP_DOWN -> -90
+ RotateType.FLIP_LEFT_RIGHT -> -180
+ else -> 0
+ }
+ setMVPMatrix(angle)
+ }
+
+ fun setTransformMatrix(matrix: FloatArray) {
+ this.mStMatrix = matrix
+ }
+
+ private fun setMVPMatrix(angle: Int): FloatArray {
+ Matrix.setIdentityM(mMVPMatrix, 0)
+ when (angle) {
+ -90 -> {
+ // 上下翻转 (绕x轴180度)
+ val radius = (180 * Math.PI / 180.0).toFloat()
+ mMVPMatrix[5] *= cos(radius.toDouble()).toFloat()
+ mMVPMatrix[6] += (-sin(radius.toDouble())).toFloat()
+ mMVPMatrix[9] += sin(radius.toDouble()).toFloat()
+ mMVPMatrix[10] *= cos(radius.toDouble()).toFloat()
+ }
+ -180 -> {
+ // 左右翻转 (绕y轴180度)
+ val radius = (180 * Math.PI / 180.0).toFloat()
+ mMVPMatrix[0] *= cos(radius.toDouble()).toFloat()
+ mMVPMatrix[2] += sin(radius.toDouble()).toFloat()
+ mMVPMatrix[8] += (-sin(radius.toDouble())).toFloat()
+ mMVPMatrix[10] *= cos(radius.toDouble()).toFloat()
+ }
+ else -> {
+ // 旋转画面(绕z轴)
+ val radius = (angle * Math.PI / 180.0).toFloat()
+ mMVPMatrix[0] *= cos(radius.toDouble()).toFloat()
+ mMVPMatrix[1] += (-sin(radius.toDouble())).toFloat()
+ mMVPMatrix[4] += sin(radius.toDouble()).toFloat()
+ mMVPMatrix[5] *= cos(radius.toDouble()).toFloat()
+ }
+ }
+ return mMVPMatrix
+ }
+
+ companion object {
+ private const val TAG = "CameraRender"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CaptureRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CaptureRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c8ead23f6e17cd414fbdf4c47de124a5ef7396cd
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/CaptureRender.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.opengl.GLES20
+import android.opengl.Matrix
+import com.jiangdg.ausbc.R
+import kotlin.math.cos
+import kotlin.math.sin
+
+/** Inherit from AbstractFboRender
+ * render preview data with preview_vertex.glsl and base_fragment.glsl
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+class CaptureRender(context: Context) : AbstractFboRender(context) {
+ private var mMVPMatrixHandle: Int = -1
+ private var mMVPMatrix = FloatArray(16)
+
+ override fun init() {
+ Matrix.setIdentityM(mMVPMatrix, 0)
+ // 上下翻转 (绕x轴180度)
+ // glReadPixels读取的是大端数据,但是我们保存的是小端
+ // 故需要将图片上下颠倒为正
+ val radius = (180 * Math.PI / 180.0).toFloat()
+ mMVPMatrix[5] *= cos(radius.toDouble()).toFloat()
+ mMVPMatrix[6] += (-sin(radius.toDouble())).toFloat()
+ mMVPMatrix[9] += sin(radius.toDouble()).toFloat()
+ mMVPMatrix[10] *= cos(radius.toDouble()).toFloat()
+ // 获取句柄
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
+ }
+
+ override fun beforeDraw() {
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0)
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.capture_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.base_fragment
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/EncodeRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/EncodeRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d30ab0bed4d2ca30c9969115dc37ceb59bf7d5cc
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/EncodeRender.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.opengl.EGLContext
+import android.view.Surface
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.env.EGLEvn
+
+/** Inherit from AbstractFboRender
+ * render data to EGL from fbo and encode it
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+class EncodeRender(context: Context): AbstractRender(context) {
+
+ private var mEgl: EGLEvn? = null
+
+ fun initEGLEvn(glContext: EGLContext) {
+ mEgl = EGLEvn()
+ mEgl?.initEgl(glContext)
+ }
+
+ fun setupSurface(surface: Surface) {
+ mEgl?.setupSurface(surface)
+ mEgl?.eglMakeCurrent()
+ }
+
+ fun swapBuffers(timeStamp: Long) {
+ mEgl?.setPresentationTime(timeStamp)
+ mEgl?.swapBuffers()
+ }
+
+ override fun clear() {
+ mEgl?.releaseElg()
+ mEgl = null
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.base_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.base_fragment
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/ScreenRender.kt b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/ScreenRender.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3df3f4b3817a770e89a99585aa0d9a388de8e5ec
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/render/internal/ScreenRender.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.render.internal
+
+import android.content.Context
+import android.view.Surface
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.render.env.EGLEvn
+/** Inherit from AbstractFboRender
+ * render data to screen from fbo with base_vertex.glsl and base_fragment.glsl
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+class ScreenRender(context: Context) : AbstractRender(context) {
+ private var mEgl: EGLEvn? = null
+
+ fun initEGLEvn() {
+ mEgl = EGLEvn()
+ mEgl?.initEgl()
+ }
+
+ fun setupSurface(surface: Surface?, surfaceWidth: Int = 0, surfaceHeight: Int = 0) {
+ mEgl?.setupSurface(surface, surfaceWidth, surfaceHeight)
+ }
+
+ fun eglMakeCurrent() {
+ mEgl?.eglMakeCurrent()
+ }
+
+ fun swapBuffers(timeStamp: Long) {
+ mEgl?.setPresentationTime(timeStamp)
+ mEgl?.swapBuffers()
+ }
+
+ fun getCurrentContext() = mEgl?.getEGLContext()
+
+ override fun clear() {
+ mEgl?.releaseElg()
+ mEgl = null
+ }
+
+ override fun getVertexSourceId(): Int = R.raw.base_vertex
+
+ override fun getFragmentSourceId(): Int = R.raw.base_fragment
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/ActivityStackUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/ActivityStackUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b7a11e8367bedb5c8a7cdcd6553b0c725da95dd3
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/ActivityStackUtils.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.app.Activity
+import java.util.*
+
+/** Activity stack manager
+ *
+ * @author Created by jiangdg on 2022/3/1
+ */
+object ActivityStackUtils {
+ private const val TAG = "ActivityStackUtils"
+ private val mStack: Stack = Stack()
+
+ fun pushActivity(activity: Activity) {
+ mStack.push(activity)
+ Logger.d(TAG, "push stack: ${activity.localClassName}")
+ }
+
+ fun popActivity() {
+ if (!mStack.empty()) {
+ val activity: Activity = mStack.pop()
+ activity.finish()
+ Logger.d(TAG, "pop stack: ${activity.localClassName}")
+ }
+ }
+
+ fun removeActivity(activity: Activity) {
+ if (!mStack.empty()) {
+ mStack.remove(activity)
+ Logger.d(TAG, "remove stack: ${activity.localClassName}")
+ }
+ }
+
+ fun getStackTop(): Activity? {
+ var activity: Activity? = null
+ if (!mStack.empty()) {
+ activity = mStack.peek()
+ Logger.d(TAG, "stack top: ${activity.localClassName}")
+ }
+ return activity
+ }
+
+ fun popAllActivity() {
+ if (!mStack.empty()) {
+ val size: Int = mStack.size
+ for (i in 0 until size) {
+ popActivity()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/AppUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/AppUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b1a6f8337d6709e2c5280fccc2454c4b23eb6c19
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/AppUtils.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.annotation.SuppressLint
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Process
+
+/** App operator utils
+ *
+ * @author Created by jiangdg on 2022/3/1
+ */
+object AppUtils {
+
+ @SuppressLint("UnspecifiedImmutableFlag")
+ fun restartApp(ctx: Context?) {
+ ctx ?: return
+ val pckgManager: PackageManager = ctx.applicationContext.packageManager
+ val intent: Intent? = pckgManager.getLaunchIntentForPackage(ctx.packageName)
+ intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(
+ ctx.applicationContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
+ )
+ val manager: AlarmManager = ctx.applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ manager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pendingIntent)
+ }
+
+ fun releaseAppResource() {
+ Process.killProcess(Process.myPid())
+ System.exit(0)
+ }
+
+ fun removeAllActivity() {
+ ActivityStackUtils.popAllActivity()
+ }
+
+ fun getAppName(ctx: Context): String? {
+ val packageManager: PackageManager = ctx.packageManager
+ try {
+ val packageInfo: PackageInfo = packageManager.getPackageInfo(ctx.packageName, 0)
+ val labelRes: Int = packageInfo.applicationInfo.labelRes
+ return ctx.getString(labelRes)
+ } catch (e: PackageManager.NameNotFoundException) {
+ e.printStackTrace()
+ }
+ return null
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/CameraUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/CameraUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4a3c2ff7e0de1f6362f072b243cccda18e5712ff
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/CameraUtils.kt
@@ -0,0 +1,76 @@
+package com.jiangdg.ausbc.utils
+
+import android.Manifest
+import android.content.Context
+import android.content.pm.PackageManager
+import android.hardware.usb.UsbConstants
+import android.hardware.usb.UsbDevice
+import androidx.core.content.ContextCompat
+import com.jiangdg.ausbc.R
+import com.serenegiant.usb.DeviceFilter
+
+/** Camera tools
+ *
+ * @author Created by jiangdg on 2022/7/19
+ */
+object CameraUtils {
+
+ /**
+ * check is usb camera
+ *
+ * @param device see [UsbDevice]
+ * @return true usb camera
+ */
+ fun isUsbCamera(device: UsbDevice?): Boolean {
+ return when (device?.deviceClass) {
+ UsbConstants.USB_CLASS_VIDEO -> {
+ true
+ }
+ UsbConstants.USB_CLASS_MISC -> {
+ var isVideo = false
+ for (i in 0 until device.interfaceCount) {
+ val cls = device.getInterface(i).interfaceClass
+ if (cls == UsbConstants.USB_CLASS_VIDEO) {
+ isVideo = true
+ break
+ }
+ }
+ isVideo
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ /**
+ * Filter needed usb device by according to filter regular
+ *
+ * @param context context
+ * @param usbDevice see [UsbDevice]
+ * @return true find success
+ */
+ fun isFilterDevice(context: Context?, usbDevice: UsbDevice?): Boolean {
+ return DeviceFilter.getDeviceFilters(context, R.xml.default_device_filter)
+ .find { devFilter ->
+ devFilter.mProductId == usbDevice?.productId && devFilter.mVendorId == usbDevice.vendorId
+ }.let { dev ->
+ dev != null
+ }
+ }
+
+ fun hasAudioPermission(ctx: Context): Boolean{
+ val locPermission = ContextCompat.checkSelfPermission(ctx, Manifest.permission.RECORD_AUDIO)
+ return locPermission == PackageManager.PERMISSION_GRANTED
+ }
+
+ fun hasStoragePermission(ctx: Context): Boolean{
+ val locPermission = ContextCompat.checkSelfPermission(ctx, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ return locPermission == PackageManager.PERMISSION_GRANTED
+ }
+
+ fun hasCameraPermission(ctx: Context): Boolean{
+ val locPermission = ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA)
+ return locPermission == PackageManager.PERMISSION_GRANTED
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/CrashUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/CrashUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e34b79b26c854c30eacf0946aaa6c0dfc109891d
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/CrashUtils.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.app.Application
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Looper
+import java.io.File
+import java.io.FileWriter
+import java.io.IOException
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.*
+
+/** Handle Crash information
+ *
+ * Crash files : /storage/emulated/0/Android/data/packagename/files
+ * or /data/data/packagename/files
+ *
+ * @author Created by jiangdg on 2022/3/1
+ */
+object CrashUtils : Thread.UncaughtExceptionHandler {
+
+ private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null
+ private var mApplication: Application? = null
+
+ fun init(application: Application) {
+ this.mApplication = application
+ mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(this);
+ }
+
+ override fun uncaughtException(t: Thread, e: Throwable) {
+ if (!handleException(e) && mDefaultHandler != null) {
+ mDefaultHandler!!.uncaughtException(t, e)
+ } else {
+ try {
+ Thread.sleep((2 * 1000).toLong())
+ } catch (e1: InterruptedException) {
+ e1.printStackTrace()
+ }
+ restartAppIfNeed()
+ }
+ }
+
+ private fun restartAppIfNeed(reboot: Boolean = false) {
+ AppUtils.apply {
+ removeAllActivity()
+ if (reboot) {
+ restartApp(mApplication?.applicationContext)
+ }
+ releaseAppResource()
+ }
+ }
+
+ private fun handleException(ex: Throwable?): Boolean {
+ if (ex == null) {
+ return false
+ }
+ saveErrorInfo(ex).apply {
+ Thread {
+ Looper.prepare()
+ ToastUtils.show("App crash, saved path ${this?.path}")
+ Looper.loop()
+ }.start()
+ uploadErrorInfo(this)
+ }
+ return true
+ }
+
+ private fun uploadErrorInfo(saveErrorInfo: File?) {
+ // upload to server
+ }
+
+ private fun saveErrorInfo(ex: Throwable): File? {
+ var logFile: File? = null
+ var fw: FileWriter? = null
+ var printWriter: PrintWriter? = null
+ try {
+ mApplication?.apply {
+ val time = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS").format(Date())
+ val crashPath = "${getExternalFilesDir(null)?.path}${File.separator}AUSBC-crash-${time}.log"
+ logFile = File(crashPath)
+ logFile ?: return null
+ fw = FileWriter(logFile, true)
+ fw ?: return null
+ printWriter = PrintWriter(fw!!)
+ val pm: PackageManager = packageManager
+ val pi: PackageInfo = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
+ printWriter?.let { pw ->
+ pw.println()
+ pw.println("Time:${SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(Date())}")
+ pw.println("VersionInfo:versionCode=${pi.versionCode} versionName:${pi.versionName}")
+ // 通过反射获取手机参数
+ pw.println("PhoneInfo :manufacture=${Build.MANUFACTURER.toString()} model=${Build.MODEL}")
+ pw.println("SystemInfo :version=${Build.VERSION.RELEASE}")
+ // 打印堆栈信息
+ ex.printStackTrace(pw)
+ pw.println("End=====================================")
+ pw.flush()
+ fw?.flush()
+ }
+ }
+ } catch (e1: IOException) {
+ e1.printStackTrace()
+ } catch (e: PackageManager.NameNotFoundException) {
+ e.printStackTrace()
+ } finally {
+ try {
+ printWriter?.close()
+ fw?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ return logFile
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/GLBitmapUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/GLBitmapUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ef984c6695d2d8a161c1349ff9482f3e10bbd82c
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/GLBitmapUtils.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.graphics.Bitmap
+import android.opengl.GLES20
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+
+/**
+ *
+ * @author Created by jiangdg on 2022/2/9
+ */
+object GLBitmapUtils {
+
+ fun transFrameBufferToBitmap(frameBufferId: Int, width: Int, height: Int): Bitmap {
+ val byteBuffer = ByteBuffer.allocateDirect(width * height * 4)
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
+ return transFrameBufferToBitmap(frameBufferId, width, height, byteBuffer)
+ }
+
+ private fun transFrameBufferToBitmap(
+ frameBufferId: Int, width: Int, height: Int,
+ byteBuffer: ByteBuffer
+ ): Bitmap {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId)
+ GLES20.glReadPixels(
+ 0,
+ 0,
+ width,
+ height,
+ GLES20.GL_RGBA,
+ GLES20.GL_UNSIGNED_BYTE,
+ byteBuffer
+ )
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ bitmap?.copyPixelsFromBuffer(byteBuffer)
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
+ return bitmap
+ }
+
+ fun readPixelToByteBuffer(
+ frameBufferId: Int, width: Int, height: Int,
+ byteBuffer: ByteBuffer?
+ ) {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId)
+ GLES20.glReadPixels(
+ 0,
+ 0,
+ width,
+ height,
+ GLES20.GL_RGBA,
+ GLES20.GL_UNSIGNED_BYTE,
+ byteBuffer
+ )
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
+ }
+
+ fun readPixelToBitmap(width: Int, height: Int): Bitmap? {
+ val byteBuffer = ByteBuffer.allocateDirect(width * height * 4)
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
+ return readPixelToBitmapWithBuffer(width, height, byteBuffer)
+ }
+
+ /**
+ * 直接readPixel保存到Bitmap, 复用byteBuffer
+ *
+ * @param width
+ * @param height
+ * @param byteBuffer
+ * @return
+ */
+ private fun readPixelToBitmapWithBuffer(width: Int, height: Int, byteBuffer: ByteBuffer?): Bitmap? {
+ if (byteBuffer == null) {
+ return null
+ }
+ byteBuffer.clear()
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
+ GLES20.glReadPixels(
+ 0,
+ 0,
+ width,
+ height,
+ GLES20.GL_RGBA,
+ GLES20.GL_UNSIGNED_BYTE,
+ byteBuffer
+ )
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ bitmap?.copyPixelsFromBuffer(byteBuffer)
+ return bitmap
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/Logger.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/Logger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..cb2f209afd4bd1870ed81ccd2821e9764c9e7886
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/Logger.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.app.Application
+import com.serenegiant.utils.XLogWrapper
+
+/** Logger utils
+ *
+ * Default log files dir: /storage/emulated/0/Android/data/packagename/files
+ * or /data/data/packagename/files
+ *
+ * @author Created by jiangdg on 2022/1/24
+ */
+object Logger {
+ fun init(application: Application, folderPath: String? = null) {
+ XLogWrapper.init(application, folderPath)
+ }
+
+ fun i(flag: String, msg: String) {
+ XLogWrapper.i(flag, msg)
+ }
+
+ fun d(flag: String, msg: String) {
+ XLogWrapper.d(flag, msg)
+ }
+
+ fun w(flag: String, msg: String) {
+ XLogWrapper.w(flag, msg)
+ }
+
+ fun w(flag: String, throwable: Throwable?) {
+ XLogWrapper.w(flag, throwable)
+ }
+
+ fun w(flag: String, msg: String, throwable: Throwable?) {
+ XLogWrapper.w(flag, msg, throwable)
+ }
+
+ fun e(flag: String, msg: String) {
+ XLogWrapper.e(flag, msg)
+ }
+
+ fun e(flag: String, msg: String, throwable: Throwable?) {
+ XLogWrapper.e(flag, msg, throwable)
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/MediaUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/MediaUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3220b82f3ef618548e32cf8044cba96293c22ce3
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/MediaUtils.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.content.Context
+import android.graphics.ImageFormat
+import android.graphics.Rect
+import android.graphics.YuvImage
+import android.net.Uri
+import android.os.Build
+import android.provider.MediaStore
+import androidx.annotation.ChecksSdkIntAtLeast
+import java.io.*
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.ShortBuffer
+
+/** Media utils
+ *
+ * @author Created by jiangdg on 2022/2/23
+ */
+object MediaUtils {
+
+ private const val TAG = "MediaUtils"
+
+ fun readRawTextFile(context: Context, rawId: Int): String {
+ val inputStream = context.resources.openRawResource(rawId)
+ val br = BufferedReader(InputStreamReader(inputStream))
+ var line: String?
+ val sb = StringBuilder()
+ try {
+ while (br.readLine().also { line = it } != null) {
+ sb.append(line)
+ sb.append("\n")
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ Logger.e(TAG, "open raw file failed!", e)
+ }
+ try {
+ br.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ Logger.e(TAG, "close raw file failed!", e)
+ }
+ return sb.toString()
+ }
+
+ fun findRecentMedia(context: Context): String? {
+ val imagePath = findRecentMedia(context, true)
+ val videoPath = findRecentMedia(context, false)
+ if (imagePath == null) {
+ return videoPath
+ }
+ if (videoPath == null) {
+ return imagePath
+ }
+ val imageFile = File(imagePath)
+ val videoFile = File(videoPath)
+ if (imageFile.lastModified() >= videoFile.lastModified()) {
+ return imagePath
+ }
+ return videoPath
+ }
+
+ fun findRecentMedia(context: Context, isImage: Boolean): String? {
+ val uri: Uri
+ val sortOrder: String
+ val columnName: String
+ val projection = if (isImage) {
+ columnName = MediaStore.Images.Media.DATA
+ sortOrder = MediaStore.Images.ImageColumns._ID + " DESC"
+ uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+ arrayOf(
+ MediaStore.Images.Media.DATA, MediaStore.Images.ImageColumns.DATE_ADDED,
+ MediaStore.Images.Media.SIZE, MediaStore.Images.Media.MIME_TYPE,
+ MediaStore.Images.Media.WIDTH, MediaStore.Images.Media.HEIGHT
+ )
+ } else {
+ columnName = MediaStore.Video.Media.DATA
+ sortOrder = MediaStore.Video.VideoColumns._ID + " DESC"
+ uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ arrayOf(
+ MediaStore.Video.Media.DATA, MediaStore.Video.Media.DISPLAY_NAME,
+ MediaStore.Video.VideoColumns.DATE_ADDED, MediaStore.Video.Media.SIZE,
+ MediaStore.Video.Media.MIME_TYPE, MediaStore.Video.Media.DURATION,
+ MediaStore.Video.Media.WIDTH, MediaStore.Video.Media.HEIGHT
+ )
+ }
+ context.contentResolver.query(
+ uri,
+ projection,
+ null,
+ null,
+ sortOrder
+ )?.apply {
+ if (count < 1) {
+ close()
+ return null
+ }
+ while (moveToNext()) {
+ val data = getString(getColumnIndexOrThrow(columnName))
+ val file = File(data)
+ if (file.exists()) {
+ close()
+ return file.path
+ }
+ }
+ }.also {
+ it?.close()
+ }
+ return null
+ }
+
+ fun saveYuv2Jpeg(path: String, data: ByteArray, width: Int, height: Int): Boolean {
+ val yuvImage = try {
+ YuvImage(data, ImageFormat.NV21, width, height, null)
+ } catch (e: Exception) {
+ Logger.e(TAG, "create YuvImage failed.", e)
+ null
+ } ?: return false
+ val bos = ByteArrayOutputStream(data.size)
+ var result = try {
+ yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, bos)
+ } catch (e: Exception) {
+ Logger.e(TAG, "compressToJpeg failed.", e)
+ false
+ }
+ if (! result) {
+ return false
+ }
+ val buffer = bos.toByteArray()
+ val file = File(path)
+ val fos: FileOutputStream?
+ try {
+ fos = FileOutputStream(file)
+ fos.write(buffer)
+ fos.close()
+ } catch (e: IOException) {
+ Logger.e(TAG, "saveYuv2Jpeg failed.", e)
+ result = false
+ e.printStackTrace()
+ } finally {
+ try {
+ bos.close()
+ } catch (e: IOException) {
+ result = false
+ Logger.e(TAG, "saveYuv2Jpeg failed.", e)
+ e.printStackTrace()
+ }
+ }
+ return result
+ }
+
+ fun transferByte2Short(data: ByteArray, readBytes: Int): ShortArray {
+ // byte[] to short[], the length of the array is reduced by half
+ val shortLen = readBytes / 2
+ // Assemble byte[] numbers as ByteBuffer buffers
+ val byteBuffer: ByteBuffer = ByteBuffer.wrap(data, 0, readBytes)
+ // Convert ByteBuffer to little endian and get shortBuffer
+ val shortBuffer: ShortBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()
+ val shortData = ShortArray(shortLen)
+ shortBuffer.get(shortData, 0, shortLen)
+ return shortData
+ }
+
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q)
+ fun isAboveQ(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ }
+}
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/SettableFuture.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/SettableFuture.kt
new file mode 100644
index 0000000000000000000000000000000000000000..533f10fb3835ceb94b9f7e7a49ec7939f3b65b5d
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/SettableFuture.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import java.util.concurrent.*
+import java.util.concurrent.locks.AbstractQueuedSynchronizer
+
+/** Sync read tool
+ *
+ * @author Created by jiangdg on 2021/12/21
+ */
+open class SettableFuture : Future {
+
+ private val sync: Sync = Sync()
+
+ // 设置结果,解除线程同步等待。
+ fun set(value: V?): Boolean {
+ return sync.set(value)
+ }
+
+ override fun isDone(): Boolean {
+ return sync.isDone()
+ }
+
+ // 获取异步结果,如果结果还没有计算出来,
+ // 则进入同步等待状态。
+ override fun get(): V? {
+ return sync.get()
+ }
+
+ // 获取异步结果,如果结果还没有计算出来,
+ // 则进入同步等待状态,一段时间之后超时。
+ override fun get(timeout: Long, unit: TimeUnit): V? {
+ return sync[unit.toNanos(timeout)]
+ }
+
+ override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
+ return sync.cancel(mayInterruptIfRunning)
+ }
+
+ override fun isCancelled(): Boolean {
+ return sync.isCancelled()
+ }
+
+ private class Sync : AbstractQueuedSynchronizer() {
+ private var value: V? = null
+ private var exception: Throwable? = null
+
+ fun isDone(): Boolean {
+ return state and (COMPLETED or CANCELLED or INTERRUPTED) != 0
+ }
+
+ fun isCancelled(): Boolean {
+ return state and (CANCELLED or INTERRUPTED) != 0
+ }
+
+ override fun tryAcquireShared(ignored: Int): Int {
+ return if (isDone()) {
+ 1
+ } else {
+ -1
+ }
+ }
+
+ override fun tryReleaseShared(finalState: Int): Boolean {
+ state = finalState
+ return true
+ }
+
+ operator fun get(nanos: Long): V? {
+ if (!tryAcquireSharedNanos(-1, nanos)) {
+ throw TimeoutException("Timeout waiting for task.")
+ }
+ return getValue()
+ }
+
+ fun get(): V? {
+ acquireSharedInterruptibly(-1)
+ return getValue()
+ }
+
+ private fun getValue(): V? {
+ when (state) {
+ COMPLETED -> return if (exception != null) {
+ throw ExecutionException(exception)
+ } else {
+ value
+ }
+ CANCELLED, INTERRUPTED -> throw cancellationExceptionWithCause(
+ "Task was cancelled.",
+ exception
+ )
+ else -> throw IllegalStateException("Error, synchronizer in invalid state: $state")
+ }
+ }
+
+ private fun cancellationExceptionWithCause(
+ message: String?,
+ cause: Throwable?
+ ): CancellationException {
+ val exception = CancellationException(message)
+ exception.initCause(cause)
+ return exception
+ }
+
+ fun wasInterrupted(): Boolean {
+ return state == INTERRUPTED
+ }
+
+ fun set(v: V?): Boolean {
+ return complete(v, null, COMPLETED)
+ }
+
+ fun setException(t: Throwable): Boolean {
+ return complete(null, t, COMPLETED)
+ }
+
+ fun cancel(interrupt: Boolean): Boolean {
+ return complete(null, null, if (interrupt) INTERRUPTED else CANCELLED)
+ }
+
+ private fun complete(v: V?, t: Throwable?, finalState: Int): Boolean {
+ val doCompletion = compareAndSetState(RUNNING, COMPLETING)
+ if (doCompletion) {
+ this.value = v
+ this.exception = if (finalState and (CANCELLED or INTERRUPTED) != 0) {
+ CancellationException("Future.cancel() was called.")
+ } else {
+ t
+ }
+ releaseShared(finalState)
+ } else if (state == COMPLETING) {
+ acquireShared(-1)
+ }
+ return doCompletion
+ }
+
+ companion object {
+ private const val RUNNING: Int = 0
+ private const val COMPLETING: Int = 1
+ private const val COMPLETED: Int = 2
+ private const val CANCELLED: Int = 4
+ private const val INTERRUPTED: Int = 8
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/SpaceItemDecoration.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/SpaceItemDecoration.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2accb213ab43000654ae35b9e5b9eefe94506bc1
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/SpaceItemDecoration.kt
@@ -0,0 +1,24 @@
+package com.jiangdg.ausbc.utils
+
+import android.graphics.Rect
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+/** RecyclerView.ItemDecoration
+ *
+ * @author Created by jiangdg on 2022/7/24
+ */
+class SpaceItemDecoration(private val space: Int): RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ outRect.left = space;
+ outRect.bottom = space;
+ if (parent.getChildLayoutPosition(view) %3==0) {
+ outRect.left = 0
+ }
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/ToastUtils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/ToastUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e49388a8b05a0f351166e90efaa396512df988af
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/ToastUtils.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.content.Context
+import android.widget.Toast
+import androidx.annotation.MainThread
+
+/** Simple Toast
+ *
+ * @author Created by jiangdongguo on 2022/1/21
+ */
+object ToastUtils {
+
+ private var applicationCtx: Context ?= null
+
+ @MainThread
+ fun init(ctx: Context) {
+ if (applicationCtx != null) {
+ return
+ }
+ this.applicationCtx = ctx.applicationContext
+ }
+
+ @JvmStatic
+ fun show(msg: String) {
+ applicationCtx?.let { ctx ->
+ Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show()
+ }
+ }
+
+ @JvmStatic
+ fun show(resId: Int) {
+ applicationCtx?.let { ctx ->
+ Toast.makeText(ctx, ctx.getString(resId), Toast.LENGTH_LONG).show()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/Utils.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/Utils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f346238b3fb8298f5e75c965b501ace4e80aef7b
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/Utils.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils
+
+import android.Manifest
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.location.Location
+import android.location.LocationManager
+import android.os.Build
+import android.os.PowerManager
+import androidx.annotation.RawRes
+import androidx.core.content.ContextCompat
+import java.io.InputStream
+
+/** Common Utils
+ *
+ * @author Created by jiangdg on 2021/12/27
+ */
+object Utils {
+
+ var debugCamera = false
+
+ fun isTargetSdkOverP(context: Context): Boolean {
+ val targetSdkVersion = try {
+ val aInfo = context.packageManager.getApplicationInfo(context.packageName, 0)
+ aInfo.targetSdkVersion
+ } catch (e: PackageManager.NameNotFoundException) {
+ return false
+ }
+ return targetSdkVersion >= Build.VERSION_CODES.P
+ }
+
+ fun getGpsLocation(context: Context?): Location? {
+ context?.let { ctx->
+ val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ val locPermission = ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION)
+ if (locPermission == PackageManager.PERMISSION_GRANTED) {
+ return locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
+ }
+ }
+ return null
+ }
+
+ fun dp2px(context: Context, dpValue: Float): Int {
+ val scale: Float = context.resources.displayMetrics.density
+ return (dpValue * scale + 0.5f).toInt()
+ }
+
+ fun wakeLock(context: Context): PowerManager.WakeLock {
+ val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
+ val mWakeLock: PowerManager.WakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "jj:camera")
+ mWakeLock.setReferenceCounted(false)
+ mWakeLock.acquire(10*60*1000L /*10 minutes*/)
+ return mWakeLock
+ }
+
+ fun wakeUnLock(wakeLock: PowerManager.WakeLock?) {
+ wakeLock?.release()
+ }
+
+ fun getGLESVersion(context: Context): String? {
+ (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).apply {
+ return deviceConfigurationInfo.glEsVersion
+ }
+ }
+
+ fun getScreenWidth(context: Context): Int {
+ return context.resources.displayMetrics.widthPixels
+ }
+
+ fun getScreenHeight(context: Context): Int {
+ return context.resources.displayMetrics.heightPixels
+ }
+
+ fun loadBitmapFromRawResource(context: Context, @RawRes id: Int): Bitmap? {
+ var inputStream: InputStream? = null
+ return try {
+ inputStream = context.resources?.openRawResource(id)
+ BitmapFactory.decodeStream(inputStream)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ null
+ } finally {
+ try {
+ inputStream?.close()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/BusKey.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/BusKey.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0c1a8300bc3552115096891974f966e813a88e8b
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/BusKey.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils.bus
+
+/** Bus keys
+ *
+ * @author Created by jiangdg on 2022/3/15
+ */
+object BusKey {
+ const val KEY_FRAME_RATE = "frame-rate"
+ const val KEY_RENDER_READY = "render-ready"
+ const val KEY_CAMERA_STATUS = "camera-status"
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/EventBus.kt b/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/EventBus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..19da55db60face7b6915c1c48a092eaac1f745c5
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/utils/bus/EventBus.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.utils.bus
+
+import androidx.annotation.MainThread
+import androidx.lifecycle.*
+import java.util.concurrent.ConcurrentHashMap
+
+/** Event bus with using [LiveData]
+ *
+ * @author Created by jiangdg on 2022/3/15
+ */
+object EventBus {
+ private val mLiveDataMap = ConcurrentHashMap>()
+
+ /** Register the event bus
+ *
+ * key represents the event bus name
+ * T represents the event type, generic
+ *
+ * @param key bus name
+ * @return [BusLiveData], data type can any object
+ */
+ fun with(key: String): BusLiveData {
+ var liveData = mLiveDataMap[key] as? BusLiveData
+ if (liveData == null) {
+ liveData = BusLiveData(key).apply {
+ mLiveDataMap[key] = this
+ }
+ }
+ return liveData
+ }
+
+ /** Customize LiveData
+ *
+ * By managing the version of LiveData by yourself, it is convenient for subsequent
+ * synchronization of the version field of the Observer
+ */
+ class BusLiveData(private val busName: String): MutableLiveData() {
+ internal var mVersion = 0
+
+ /**
+ * Send message to event bus,only used in UI thread
+ *
+ * @param message event, can be any object
+ */
+ @MainThread
+ fun sendMessage(message: T) {
+ ++mVersion
+ value = message
+ }
+
+ /**
+ * Send message to event bus, be used in any thread
+ *
+ * @param message event, can be any object
+ */
+ fun postMessage(message: T) {
+ ++mVersion
+ postValue(message)
+ }
+
+ override fun observe(owner: LifecycleOwner, observer: Observer) {
+ // Listen for the destruction event of the host, and check whether there are other observers in LiveData
+ // If the LiveData is not removed actively
+ owner.lifecycle.addObserver(object : LifecycleEventObserver {
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ if (event == Lifecycle.Event.ON_DESTROY) {
+ if (mLiveDataMap[busName]?.hasObservers() == false) {
+ mLiveDataMap.remove(busName)
+ }
+ }
+ }
+ })
+ // Repackage the observer
+ // use a proxy observer, which will only dispatch the latest events to the observer
+ super.observe(owner, ProxyObserver(this, observer))
+ }
+ }
+
+ /** Proxy Observer
+ *
+ * Control whether to distribute events by managing the version field of the Observer yourself
+ */
+ internal class ProxyObserver(
+ private val liveData: BusLiveData,
+ private val observer: Observer
+ ): Observer {
+ // Initialize the version of Observer to be consistent with that of LiveData
+ private var mLastVersion = liveData.mVersion
+
+ // Only when LiveData has the latest data, the onChanged dispatch event of the Observer is called
+ // Among them, when judging the condition of new data: LiveData.version > Observer.version
+ override fun onChanged(data: T) {
+ if (mLastVersion >= liveData.mVersion) {
+ return
+ }
+ mLastVersion = liveData.mVersion
+ observer.onChanged(data)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioGLSurfaceView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioGLSurfaceView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2425500989df87ce7e228236a24a324d112a8834
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioGLSurfaceView.kt
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.SurfaceTexture
+import android.opengl.GLES11Ext
+import android.opengl.GLES20
+import android.opengl.GLSurfaceView
+import android.opengl.Matrix
+import android.os.Handler
+import android.os.Looper
+import android.util.AttributeSet
+import android.view.Surface
+import com.jiangdg.ausbc.R
+import com.jiangdg.ausbc.utils.Logger
+import com.jiangdg.ausbc.utils.MediaUtils
+import javax.microedition.khronos.egl.EGLConfig
+import javax.microedition.khronos.opengles.GL10
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.FloatBuffer
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.sin
+
+/** 纵横比自适应GLSurfaceView
+ *
+ * @author Created by jiangdg on 2021/12/23
+ */
+class AspectRatioGLSurfaceView : GLSurfaceView, GLSurfaceView.Renderer,
+ SurfaceTexture.OnFrameAvailableListener, IAspectRatio {
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet)
+
+ private var mAspectRatio = -1.0
+ private var mSurfaceTexture: SurfaceTexture? = null
+ private var mVertexBuffer: FloatBuffer? = null
+ private var mListener: OnSurfaceLifecycleListener? = null
+ private var mVertexShader = 0
+ private var mFragmentShader = 0
+ private var mProgram = 0
+ private var mESOTextureId = 0
+ private val mStMatrix = FloatArray(16)
+ private val mMVPMatrix = FloatArray(16)
+ private val mUIHandler = Handler(Looper.getMainLooper())
+ private var mPositionLocation = 0
+ private var mTextureCoordLocation = 0
+ private var mStMatrixHandle = 0
+ private var mMVPMatrixHandle = 0
+
+ init {
+ setEGLContextClientVersion(2)
+ setRenderer(this)
+ renderMode = RENDERMODE_WHEN_DIRTY
+ mVertexBuffer = ByteBuffer.allocateDirect(VERTEX_DATA.size * 4).order(ByteOrder.nativeOrder())
+ .asFloatBuffer()
+ mVertexBuffer?.put(VERTEX_DATA)?.position(0)
+ Matrix.setIdentityM(mStMatrix, 0)
+ }
+
+ fun setOnSurfaceLifecycleListener(listener: OnSurfaceLifecycleListener?) {
+ mListener = listener
+ }
+
+ private fun initSurface() {
+ mESOTextureId = createExternalTexture()
+ mSurfaceTexture = SurfaceTexture(mESOTextureId)
+ mSurfaceTexture!!.setOnFrameAvailableListener(this)
+ }
+
+ private fun initGLESEngine() {
+ mProgram = createProgram()
+ if (mProgram == 0) {
+ deInitSurface()
+ return
+ }
+ mPositionLocation = GLES20.glGetAttribLocation(mProgram, "aPosition")
+ mTextureCoordLocation = GLES20.glGetAttribLocation(mProgram, "aTextureCoordinate")
+ mStMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uStMatrix")
+ mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
+ if (isGLESStatusError()) {
+ deInitSurface()
+ Logger.e(TAG, "create external texture failed")
+ return
+ }
+ Logger.i(TAG, "init opengl es success. ")
+ }
+
+ override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
+ Logger.i(TAG, "onSurfaceCreated")
+ initSurface()
+ initGLESEngine()
+ mUIHandler.post {
+ if (mListener != null) {
+ mListener!!.onSurfaceCreated(mSurfaceTexture)
+ }
+ }
+ }
+
+ override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
+ Logger.i(TAG, "onSurfaceChanged, width=$width ,height=$height")
+ GLES20.glViewport(0, 0, width, height)
+ }
+
+ override fun onDrawFrame(gl: GL10?) {
+ // 更新纹理
+ if (mSurfaceTexture != null) {
+ mSurfaceTexture!!.updateTexImage()
+ mSurfaceTexture!!.getTransformMatrix(mStMatrix)
+ }
+
+ // 绘制纹理
+ GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mESOTextureId)
+ mVertexBuffer!!.position(0)
+ GLES20.glVertexAttribPointer(
+ mPositionLocation,
+ 3,
+ GLES20.GL_FLOAT,
+ false,
+ 20,
+ mVertexBuffer
+ )
+ GLES20.glEnableVertexAttribArray(mPositionLocation)
+ mVertexBuffer!!.position(3)
+ GLES20.glVertexAttribPointer(
+ mTextureCoordLocation,
+ 2,
+ GLES20.GL_FLOAT,
+ false,
+ 20,
+ mVertexBuffer
+ )
+ GLES20.glEnableVertexAttribArray(mTextureCoordLocation)
+ GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, getMVPMatrix(), 0)
+ GLES20.glUniformMatrix4fv(mStMatrixHandle, 1, false, mStMatrix, 0)
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0)
+
+ Logger.i(TAG, "--------draw a frame---------")
+ }
+
+ private fun getMVPMatrix(): FloatArray {
+ Matrix.setIdentityM(mMVPMatrix, 0)
+ val radius = (0 * Math.PI / 180.0).toFloat()
+ mMVPMatrix[0] *= cos(radius.toDouble()).toFloat()
+ mMVPMatrix[1] += (-sin(radius.toDouble())).toFloat()
+ mMVPMatrix[4] += sin(radius.toDouble()).toFloat()
+ mMVPMatrix[5] *= cos(radius.toDouble()).toFloat()
+ return mMVPMatrix
+ }
+
+ override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
+ // 新的一帧到来
+ // 会触发GLSurfaceView.Renderer#onDraw()绘制
+ requestRender()
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ deInitSurface()
+ deInitGLESEngine()
+ mUIHandler.post {
+ if (mListener != null) {
+ mListener!!.onSurfaceDestroyed()
+ }
+ }
+ }
+
+ private fun loadShader(shaderType: Int, source: String): Int {
+ val shader = GLES20.glCreateShader(shaderType)
+ GLES20.glShaderSource(shader, source)
+ GLES20.glCompileShader(shader)
+ val compiled = IntArray(1)
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0)
+ if (compiled[0] == 0) {
+ Logger.e(TAG, "Could not compile shader $shaderType:")
+ GLES20.glDeleteShader(shader)
+ return 0
+ }
+ return shader
+ }
+
+ private fun createProgram(): Int {
+ // 创建顶点、片段着色器
+ mVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MediaUtils.readRawTextFile(context, R.raw.camera_vertex))
+ if (mVertexShader == 0) {
+ return 0
+ }
+ Logger.i(TAG, "load vertex shader success, id = $mVertexShader")
+ mFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, MediaUtils.readRawTextFile(context, R.raw.camera_fragment))
+ if (mFragmentShader == 0) {
+ return 0
+ }
+ Logger.i(TAG, "load fragment shader success, id = $mFragmentShader")
+ // 创建、链接程序,并将着色器依附到程序
+ val program = GLES20.glCreateProgram()
+ GLES20.glAttachShader(program, mVertexShader)
+ GLES20.glAttachShader(program, mFragmentShader)
+ GLES20.glLinkProgram(program)
+ val linkStatus = IntArray(1)
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Logger.e(TAG, "Could not link program, err = " + linkStatus[0])
+ return 0
+ }
+ // 使用程序
+ GLES20.glUseProgram(program)
+ Logger.i(TAG, "create and link program success, id = $program")
+ return program
+ }
+
+ private fun deInitSurface() {
+ if (mSurfaceTexture != null) {
+ mSurfaceTexture!!.setOnFrameAvailableListener(null)
+ mSurfaceTexture!!.release()
+ mSurfaceTexture = null
+ }
+ }
+
+ private fun deInitGLESEngine() {
+ if (mVertexShader != 0) {
+ GLES20.glDeleteShader(mVertexShader)
+ mVertexShader = 0
+ }
+ if (mFragmentShader != 0) {
+ GLES20.glDeleteShader(mFragmentShader)
+ mFragmentShader = 0
+ }
+ if (mProgram != 0) {
+ GLES20.glDeleteProgram(mProgram)
+ mProgram = 0
+ }
+ Logger.i(TAG, "release opengl es success")
+ }
+
+ private fun isGLESStatusError(): Boolean {
+ return GLES20.glGetError() != GLES20.GL_NO_ERROR
+ }
+
+ interface OnSurfaceLifecycleListener {
+ fun onSurfaceCreated(surface: SurfaceTexture?)
+ fun onSurfaceDestroyed()
+ }
+
+ private fun createExternalTexture(): Int {
+ val textures = IntArray(1)
+ GLES20.glGenTextures(1, textures, 0)
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0])
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat())
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
+ Logger.i(TAG, "create external texture success, texture id = " + textures[0])
+ return textures[0]
+ }
+
+ fun setDefaultBufferSize(width: Int, height: Int) {
+ mSurfaceTexture?.setDefaultBufferSize(width, height)
+ }
+
+ override fun setAspectRatio(width: Int, height: Int) {
+ val orientation = context.resources.configuration.orientation
+ // 处理竖屏和横屏情况
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ setAspectRatio(height.toDouble() / width)
+ return
+ }
+ setAspectRatio(width.toDouble() / height)
+ }
+
+ override fun getSurfaceWidth(): Int = width
+
+ override fun getSurfaceHeight(): Int = height
+
+ override fun getSurface(): Surface = holder.surface
+
+ override fun postUITask(task: () -> Unit) {
+ post {
+ task()
+ }
+ }
+
+ private fun setAspectRatio(aspectRatio: Double) {
+ if (aspectRatio < 0 || mAspectRatio == aspectRatio) {
+ return
+ }
+ mAspectRatio = aspectRatio
+ Logger.i(TAG, "AspectRatio = $mAspectRatio")
+ requestLayout()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ var initialWidth = MeasureSpec.getSize(widthMeasureSpec)
+ var initialHeight = MeasureSpec.getSize(heightMeasureSpec)
+ val horizontalPadding = paddingLeft - paddingRight
+ val verticalPadding = paddingTop - paddingBottom
+ initialWidth -= horizontalPadding
+ initialHeight -= verticalPadding
+ // 比较预览与TextureView(内容)纵横比
+ // 如果有变化,重新设置TextureView尺寸
+ val viewAspectRatio = initialWidth.toDouble() / initialHeight
+ val diff = mAspectRatio / viewAspectRatio - 1
+ var wMeasureSpec = widthMeasureSpec
+ var hMeasureSpec = heightMeasureSpec
+ if (mAspectRatio > 0 && abs(diff) > 0.01) {
+ // diff > 0, 按宽缩放
+ // diff < 0, 按高缩放
+ if (diff > 0) {
+ initialHeight = (initialWidth / mAspectRatio).toInt()
+ } else {
+ initialWidth = (initialHeight * mAspectRatio).toInt()
+ }
+ // 重新设置TextureView尺寸
+ // 注意加回padding大小
+ initialWidth += horizontalPadding
+ initialHeight += verticalPadding
+ wMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY)
+ hMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY)
+ }
+ super.onMeasure(wMeasureSpec, hMeasureSpec)
+ }
+
+ companion object {
+ private const val TAG = "AspectRatioGLSurfaceView"
+
+ private val VERTEX_DATA = floatArrayOf(
+ // X, Y, Z, U, V
+ -1.0f, -1.0f, 0f, 0f, 0f, // 右上
+ 1.0f, -1.0f, 0f, 1f, 0f, // 右下
+ -1.0f, 1.0f, 0f, 0f, 1f, // 左下
+ 1.0f, 1.0f, 0f, 1f, 1f
+ )
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioSurfaceView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioSurfaceView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..66cfdf8de25e40164122831afb08d7c60f968df3
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioSurfaceView.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.view.Surface
+import android.view.SurfaceView
+import com.jiangdg.ausbc.utils.Logger
+import kotlin.math.abs
+
+/** Adaptive SurfaceView
+ * Aspect ratio (width:height, such as 4:3, 16:9).
+ *
+ * @author Created by jiangdg on 2022/01/23
+ */
+class AspectRatioSurfaceView: SurfaceView, IAspectRatio {
+
+ private var mAspectRatio = -1.0
+
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
+ constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
+
+ override fun setAspectRatio(width: Int, height: Int) {
+ val orientation = context.resources.configuration.orientation
+ // 处理竖屏和横屏情况
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ setAspectRatio(height.toDouble() / width)
+ return
+ }
+ setAspectRatio(width.toDouble() / height)
+ }
+
+ override fun getSurfaceWidth(): Int = width
+
+ override fun getSurfaceHeight(): Int = height
+
+ override fun getSurface(): Surface = holder.surface
+
+ override fun postUITask(task: () -> Unit) {
+ post {
+ task()
+ }
+ }
+
+ private fun setAspectRatio(aspectRatio: Double) {
+ if (aspectRatio < 0 || mAspectRatio == aspectRatio) {
+ return
+ }
+ mAspectRatio = aspectRatio
+ Logger.i(TAG, "AspectRatio = $mAspectRatio")
+ requestLayout()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ var initialWidth = MeasureSpec.getSize(widthMeasureSpec)
+ var initialHeight = MeasureSpec.getSize(heightMeasureSpec)
+ val horizontalPadding = paddingLeft - paddingRight
+ val verticalPadding = paddingTop - paddingBottom
+ initialWidth -= horizontalPadding
+ initialHeight -= verticalPadding
+ // 比较预览与TextureView(内容)纵横比
+ // 如果有变化,重新设置TextureView尺寸
+ val viewAspectRatio = initialWidth.toDouble() / initialHeight
+ val diff = mAspectRatio / viewAspectRatio - 1
+ var wMeasureSpec = widthMeasureSpec
+ var hMeasureSpec = heightMeasureSpec
+ if (mAspectRatio > 0 && abs(diff) > 0.01) {
+ // diff > 0, 按宽缩放
+ // diff < 0, 按高缩放
+ if (diff > 0) {
+ initialHeight = (initialWidth / mAspectRatio).toInt()
+ } else {
+ initialWidth = (initialHeight * mAspectRatio).toInt()
+ }
+ // 重新设置TextureView尺寸
+ // 注意加回padding大小
+ initialWidth += horizontalPadding
+ initialHeight += verticalPadding
+ wMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY)
+ hMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY)
+ }
+ super.onMeasure(wMeasureSpec, hMeasureSpec)
+ }
+
+ companion object {
+ private const val TAG = "AspectRatioTextureView"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..268e80fa958c26cc1c2e0cb9bba39f8463cad842
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/AspectRatioTextureView.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.view.Surface
+import android.view.TextureView
+import com.jiangdg.ausbc.utils.Logger
+import kotlin.math.abs
+
+/** Adaptive TextureView
+ * Aspect ratio (width:height, such as 4:3, 16:9).
+ *
+ * @author Created by jiangdg on 2021/12/23
+ */
+class AspectRatioTextureView: TextureView, IAspectRatio {
+
+ private var mAspectRatio = -1.0
+
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
+ constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
+
+ override fun setAspectRatio(width: Int, height: Int) {
+ val orientation = context.resources.configuration.orientation
+ // 处理竖屏和横屏情况
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ setAspectRatio(height.toDouble() / width)
+ return
+ }
+ setAspectRatio(width.toDouble() / height)
+ }
+
+ override fun getSurfaceWidth(): Int = width
+
+ override fun getSurfaceHeight(): Int = height
+
+ override fun getSurface(): Surface? {
+ return try {
+ Surface(surfaceTexture)
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ override fun postUITask(task: () -> Unit) {
+ post {
+ task()
+ }
+ }
+
+ private fun setAspectRatio(aspectRatio: Double) {
+ if (aspectRatio < 0 || mAspectRatio == aspectRatio) {
+ return
+ }
+ mAspectRatio = aspectRatio
+ Logger.i(TAG, "AspectRatio = $mAspectRatio")
+ requestLayout()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ var initialWidth = MeasureSpec.getSize(widthMeasureSpec)
+ var initialHeight = MeasureSpec.getSize(heightMeasureSpec)
+ val horizontalPadding = paddingLeft - paddingRight
+ val verticalPadding = paddingTop - paddingBottom
+ initialWidth -= horizontalPadding
+ initialHeight -= verticalPadding
+ // 比较预览与TextureView(内容)纵横比
+ // 如果有变化,重新设置TextureView尺寸
+ val viewAspectRatio = initialWidth.toDouble() / initialHeight
+ val diff = mAspectRatio / viewAspectRatio - 1
+ var wMeasureSpec = widthMeasureSpec
+ var hMeasureSpec = heightMeasureSpec
+ if (mAspectRatio > 0 && abs(diff) > 0.01) {
+ // diff > 0, 按宽缩放
+ // diff < 0, 按高缩放
+ if (diff > 0) {
+ initialHeight = (initialWidth / mAspectRatio).toInt()
+ } else {
+ initialWidth = (initialHeight * mAspectRatio).toInt()
+ }
+ // 重新设置TextureView尺寸
+ // 注意加回padding大小
+ initialWidth += horizontalPadding
+ initialHeight += verticalPadding
+ wMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY)
+ hMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY)
+ }
+ super.onMeasure(wMeasureSpec, hMeasureSpec)
+ }
+
+ companion object {
+ private const val TAG = "AspectRatioTextureView"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/CaptureMediaView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/CaptureMediaView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4ff4408decf371b39b9ff526bb9e5a5624281f3b
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/CaptureMediaView.kt
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.RectF
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import com.jiangdg.ausbc.R
+import java.text.DecimalFormat
+import java.text.DecimalFormatSymbols
+
+/** Custom capture view
+ *
+ * @author Created by jiangdg on 2022/2/9
+ */
+class CaptureMediaView : View {
+ private var mAnimator: ObjectAnimator? = null
+
+ enum class CaptureVideoState {
+ DOING, UNDO, PAUSE
+ }
+
+ enum class CaptureMode {
+ MODE_CAPTURE_PIC, MODE_CAPTURE_VIDEO, MODE_CAPTURE_AUDIO
+ }
+
+ enum class CaptureViewTheme {
+ THEME_BLUE, THEME_WHITE
+ }
+
+ private lateinit var mPaint: Paint
+ private var mWidth = 0
+ private var mHeight = 0
+ private var circleX = 0
+ private var circleY = 0
+ private var radius = 0
+ private var mCaptureVideoState: CaptureVideoState? = null
+ private var mCaptureModel: CaptureMode? = null
+ private var mCaptureViewTheme: CaptureViewTheme? = null
+ private var mCaptureVideoDuration = 0
+ private var mCaptureVideoProgress = 0
+ private var internalCirclePercent = 0f
+ private var listener: OnViewClickListener? = null
+ private var mFirstDraw = true
+
+ interface OnViewClickListener {
+ fun onViewClick(mode: CaptureMode?)
+ }
+
+ fun setOnViewClickListener(listener: OnViewClickListener?) {
+ this.listener = listener
+ }
+
+ constructor(context: Context) : super(context) {
+ init()
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ init()
+ }
+
+ private fun init() {
+ mPaint = Paint()
+ mPaint.isAntiAlias = true
+ mCaptureVideoState = CaptureVideoState.UNDO
+ mCaptureModel = CaptureMode.MODE_CAPTURE_PIC
+ mCaptureViewTheme = CaptureViewTheme.THEME_BLUE
+ mCaptureVideoProgress = 0
+ internalCirclePercent = 1.0f
+ mCaptureVideoDuration = 60
+ }
+
+ /**
+ * 设置视频录制进度
+ *
+ * @param progress 进度值,<=mCaptureVideoDuration
+ */
+ fun setCaptureVideoProgress(progress: Int) {
+ mCaptureVideoProgress = progress
+ invalidate()
+ }
+
+ /**
+ * 设置视频录制总时长
+ *
+ * @param duration 总时长,单位为秒
+ * 默认录制60s
+ */
+ fun setCaptureVideoDuration(duration: Int) {
+ mCaptureVideoDuration = duration
+ }
+
+ /**
+ * 设置拍摄模式
+ *
+ * @param model 拍照 or 录像
+ */
+ fun setCaptureMode(model: CaptureMode?) {
+ mCaptureModel = model
+ invalidate()
+ }
+
+ /**
+ * 设置按钮风格
+ *
+ * @param theme 风格,目前支持蓝色系和白色系两种
+ */
+ fun setCaptureViewTheme(theme: CaptureViewTheme?) {
+ mCaptureViewTheme = theme
+ }
+
+ /**
+ * 设置录制状态
+ *
+ * @param state 开始录制 or 停止录制
+ */
+ fun setCaptureVideoState(state: CaptureVideoState?) {
+ mCaptureVideoState = state
+ invalidate()
+ }
+
+ /**
+ * 拍照时内部圆形缩放比例,仅供做动画使用
+ *
+ * @param internalCirclePercent 缩放比例,0f~1.0f(1.0f表示正常大小)
+ */
+ private fun setInternalCirclePercent(internalCirclePercent: Float) {
+ this.internalCirclePercent = internalCirclePercent
+ invalidate()
+ }
+
+ private fun getInternalCirclePercent(): Float {
+ return internalCirclePercent
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (event.action == MotionEvent.ACTION_UP) {
+ if (listener != null) {
+ showClickAnimation()
+ listener!!.onViewClick(mCaptureModel)
+ }
+ }
+ return true
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec))
+ }
+
+ private fun measureHeight(widthMeasureSpec: Int): Int {
+ var measureW: Int
+ val specMode = MeasureSpec.getMode(widthMeasureSpec)
+ val specSize = MeasureSpec.getSize(widthMeasureSpec)
+ if (specMode == MeasureSpec.EXACTLY) {
+ // 精度模式
+ measureW = specSize
+ } else {
+ // 默认大小
+ measureW = dp2px(80f)
+ // wrap_content
+ if (specMode == MeasureSpec.AT_MOST) {
+ measureW = measureW.coerceAtMost(specSize)
+ }
+ }
+ return measureW
+ }
+
+ private fun measureWidth(heightMeasureSpec: Int): Int {
+ var measureH: Int
+ val specMode = MeasureSpec.getMode(heightMeasureSpec)
+ val specSize = MeasureSpec.getSize(heightMeasureSpec)
+ if (specMode == MeasureSpec.EXACTLY) {
+ // 精度模式
+ measureH = specSize
+ } else {
+ // 默认大小
+ measureH = dp2px(80f)
+ // wrap_content
+ if (specMode == MeasureSpec.AT_MOST) {
+ measureH = measureH.coerceAtMost(specSize)
+ }
+ }
+ return measureH
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ // 当View大小变化时,获取其宽高
+ mWidth = width
+ mHeight = height
+ circleX = mWidth / 2
+ circleY = mHeight / 2
+ radius = mWidth / 2
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ if (mCaptureModel == CaptureMode.MODE_CAPTURE_VIDEO || mCaptureModel == CaptureMode.MODE_CAPTURE_AUDIO) {
+ drawCaptureVideo(canvas)
+ } else {
+ drawCapturePicture(canvas)
+ }
+ if (mFirstDraw) {
+ mFirstDraw = false
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ if (mAnimator != null && mAnimator!!.isRunning) {
+ mAnimator!!.cancel()
+ mAnimator = null
+ }
+ }
+
+ private fun showClickAnimation() {
+ if (mCaptureModel == CaptureMode.MODE_CAPTURE_PIC) {
+ mAnimator = ObjectAnimator.ofFloat(this, "internalCirclePercent", 1.0f, 0.85f, 1.0f)
+ mAnimator?.duration = 150
+ mAnimator?.start()
+ }
+ }
+
+ private fun drawCapturePicture(canvas: Canvas) {
+ if (mCaptureViewTheme == CaptureViewTheme.THEME_BLUE) {
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat(), mPaint)
+ // 绘制内部圆形
+ mPaint.style = Paint.Style.FILL
+ mPaint.strokeWidth = 2f
+ mPaint.color = Color.parseColor("#2E5BFF")
+ canvas.drawCircle(
+ circleX.toFloat(), circleY.toFloat(),
+ (radius - radius * 0.2).toFloat() * internalCirclePercent, mPaint
+ )
+ // 绘制外部圆环
+ mPaint.strokeWidth = (radius * 0.1).toFloat()
+ mPaint.style = Paint.Style.STROKE
+ mPaint.color = Color.parseColor("#2E5BFF")
+ canvas.drawCircle(
+ circleX.toFloat(), circleY.toFloat(),
+ (radius - radius * 0.05).toFloat(), mPaint
+ )
+ } else {
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.TRANSPARENT
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat(), mPaint)
+ // 绘制内部圆形
+ mPaint.style = Paint.Style.FILL
+ mPaint.strokeWidth = 2f
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(
+ circleX.toFloat(), circleY.toFloat(),
+ (radius - radius * 0.2).toFloat() * internalCirclePercent, mPaint
+ )
+ // 绘制外部圆环
+ mPaint.strokeWidth = (radius * 0.1).toFloat()
+ mPaint.style = Paint.Style.STROKE
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(
+ circleX.toFloat(), circleY.toFloat(),
+ (radius - radius * 0.05).toFloat(), mPaint
+ )
+ }
+ }
+
+ private fun drawCaptureVideo(canvas: Canvas) {
+ when (mCaptureVideoState) {
+ CaptureVideoState.DOING -> {
+ drawCaptureVideoDoingState(canvas)
+ }
+ CaptureVideoState.PAUSE -> {
+ drawCaptureVideoPauseState(canvas)
+ }
+ else -> {
+ drawCaptureVideoUndoState(canvas)
+ }
+ }
+ }
+
+ private fun drawCaptureVideoDoingState(canvas: Canvas) {
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = resources.getColor(R.color.common_30_black)
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat(), mPaint)
+
+ // 绘制内部白色圆形
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius * 0.575f, mPaint)
+
+ // 绘制内部暂停的两条竖线
+ mPaint.style = Paint.Style.FILL
+ mPaint.strokeWidth = 2f
+ mPaint.color = Color.parseColor("#FF0000")
+ canvas.drawRoundRect(
+ RectF(
+ (mWidth * 0.4125).toFloat(), (mWidth * 0.3875).toFloat(),
+ (mWidth * 0.4625).toFloat(), (mWidth * 0.6125).toFloat()
+ ), 8f, 8f, mPaint
+ )
+ canvas.drawRoundRect(
+ RectF(
+ (mWidth * 0.5375).toFloat(), (mWidth * 0.3875).toFloat(),
+ (mWidth * 0.5875).toFloat(), (mWidth * 0.6125).toFloat()
+ ), 8f, 8f, mPaint
+ )
+
+ // 绘制外部进度条
+ // 圆角线
+ mPaint.strokeCap = Paint.Cap.ROUND
+ mPaint.strokeWidth = (radius * 0.08).toFloat()
+ mPaint.style = Paint.Style.STROKE
+ mPaint.color = Color.parseColor("#2E5BFF")
+ val rectF = RectF(
+ (radius * 0.03).toFloat(), (radius * 0.03).toFloat(),
+ 2 * radius - (radius * 0.03).toFloat(), 2 * radius - (radius * 0.03).toFloat()
+ )
+ val dfs = DecimalFormatSymbols()
+ dfs.decimalSeparator = '.'
+ val format = DecimalFormat("0.00")
+ format.decimalFormatSymbols = dfs
+ val result =
+ format.format((mCaptureVideoProgress.toFloat() / mCaptureVideoDuration).toDouble())
+ canvas.drawArc(rectF, 270f, (result.toFloat() * 360), false, mPaint)
+ }
+
+ private fun drawCaptureVideoPauseState(canvas: Canvas) {
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = resources.getColor(R.color.common_30_black)
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat(), mPaint)
+
+ // 绘制内部白色圆形
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius * 0.6f, mPaint)
+
+ // 绘制外部进度条
+ // 圆角线
+ mPaint.strokeCap = Paint.Cap.ROUND
+ mPaint.strokeWidth = (radius * 0.08).toFloat()
+ mPaint.style = Paint.Style.STROKE
+ mPaint.color = Color.parseColor("#2E5BFF")
+ val rectF = RectF(
+ (radius * 0.03).toFloat(), (radius * 0.03).toFloat(),
+ 2 * radius - (radius * 0.03).toFloat(), 2 * radius - (radius * 0.03).toFloat()
+ )
+ val dfs = DecimalFormatSymbols()
+ dfs.decimalSeparator = '.'
+ val format = DecimalFormat("0.00")
+ format.decimalFormatSymbols = dfs
+ val result =
+ format.format((mCaptureVideoProgress.toFloat() / mCaptureVideoDuration).toDouble())
+ canvas.drawArc(rectF, 270f, (result.toFloat() * 360), false, mPaint)
+ }
+
+ private fun drawCaptureVideoUndoState(canvas: Canvas) {
+ mCaptureVideoProgress = 0
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.TRANSPARENT
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat(), mPaint)
+ // 绘制内部圆形
+ mPaint.strokeWidth = 2f
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.WHITE
+ canvas.drawCircle(
+ circleX.toFloat(), circleY.toFloat(),
+ (radius - radius * 0.3).toFloat(), mPaint
+ )
+ // 绘制发散线段
+ canvas.save()
+ mPaint.strokeWidth = dp2px(2f).toFloat()
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.WHITE
+ val startX = mWidth.toFloat() / 2
+ val startY = (mHeight / 2 - radius).toFloat()
+ val count = 30
+ for (i in 0 until count) {
+ canvas.drawLine(startX, startY, startX, startY + 10, mPaint)
+ canvas.rotate(360.toFloat() / count, width.toFloat() / 2, height.toFloat() / 2)
+ }
+ canvas.restore()
+ }
+
+ private fun dp2px(dpValue: Float): Int {
+ val scale = resources.displayMetrics.density
+ return (dpValue * scale + 0.5f).toInt()
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/CircleProgressView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/CircleProgressView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8c6c169d71955f52b6e40299aef650c5286f5fa3
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/CircleProgressView.kt
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.TypedArray
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import com.jiangdg.ausbc.R
+
+import java.text.DecimalFormat
+
+/**
+ *
+ * @author Created by jiangdg on 2022/2/8
+ */
+class CircleProgressView : View {
+ private var isRecordVideo: Boolean = false
+ private var mPaint: Paint? = null
+ private var mWidth = 0
+ private var mHeight = 0
+ private var circleX = 0
+ private var circleY = 0
+ private var radius = 0
+ private var state = 0
+ private var mSweepAngle = 1
+ private var isOddNumber = true
+ private var outsideCircleBgColor = 0
+ private var progressArcBgColor = 0
+ private var insideCircleBgColor = 0
+ private var insideCircleTouchedBgColor = 0
+ private var insideRectangleBgColor = 0
+ private var tipTextSize = 0f
+ private var tipTextColor = 0
+
+ // 进度值
+ private var progress = 0
+ private var totalSize = 0
+ private var isShowTextTip = false
+ private var isTouched = false
+
+ // 点击事件回调
+ private var listener: OnViewClickListener? = null
+ private var isDisabled = false
+
+ constructor(context: Context?) : super(context)
+
+ interface OnViewClickListener {
+ fun onViewClick()
+ }
+
+ // 点击事件回调
+ fun setOnViewClickListener(listener: OnViewClickListener?) {
+ this.listener = listener
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ // 获取自定义属性
+ val ta: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView)
+ outsideCircleBgColor = ta.getColor(
+ R.styleable.CircleProgressView_outsideCircleBgColor,
+ resources.getColor(R.color.colorWhite)
+ )
+ progressArcBgColor = ta.getColor(
+ R.styleable.CircleProgressView_progressArcBgColor,
+ resources.getColor(R.color.colorGray)
+ )
+ insideCircleBgColor = ta.getColor(
+ R.styleable.CircleProgressView_insideCircleBgColor,
+ resources.getColor(R.color.colorRed)
+ )
+ insideCircleTouchedBgColor = ta.getColor(
+ R.styleable.CircleProgressView_insideCircleTouchedBgColor,
+ resources.getColor(R.color.colorDeepRed)
+ )
+ insideRectangleBgColor = ta.getColor(
+ R.styleable.CircleProgressView_insideRectangleBgColor,
+ resources.getColor(R.color.colorRed)
+ )
+ tipTextColor = ta.getColor(
+ R.styleable.CircleProgressView_tipTextColor,
+ resources.getColor(R.color.colorWhite)
+ )
+ tipTextSize = ta.getDimension(R.styleable.CircleProgressView_tipTextSize, 34F)
+ ta.recycle()
+ mPaint = Paint()
+ }
+
+ fun setConnectState(state: Int) {
+ this.state = state
+ // 重新绘制View
+ this.invalidate()
+ }
+
+ fun getConnectState(): Int {
+ return state
+ }
+
+ fun setProgressVaule(progress: Int) {
+ this.progress = progress
+ // 重新绘制View
+ this.invalidate()
+ }
+
+ fun setTotalSize(totalSize: Int) {
+ this.totalSize = totalSize
+ }
+
+ fun setShowTextTipFlag(isShowTextTip: Boolean) {
+ this.isShowTextTip = isShowTextTip
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (listener == null || isDisabled()) return super.onTouchEvent(event)
+ when(event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ isTouched = true
+ }
+ MotionEvent.ACTION_UP -> {
+ isTouched = false
+ // 松开手时,处理触摸事件
+ listener!!.onViewClick()
+ }
+ else -> {}
+ }
+ this.invalidate()
+ return true
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ // 调用setMeasuredDimension
+ // 测量View大小
+ setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec))
+ }
+
+ private fun measureHeight(widthMeasureSpec: Int): Int {
+ var width: Int
+ val specMode: Int = MeasureSpec.getMode(widthMeasureSpec)
+ val specSize: Int = MeasureSpec.getSize(widthMeasureSpec)
+ if (specMode == MeasureSpec.EXACTLY) {
+ // 精度模式
+ width = specSize
+ } else {
+ // 默认大小
+ width = 200
+ // wrap_content
+ if (specMode == MeasureSpec.AT_MOST) {
+ width = width.coerceAtMost(specSize)
+ }
+ }
+ return width
+ }
+
+ private fun measureWidth(heightMeasureSpec: Int): Int {
+ var height: Int
+ val specMode: Int = MeasureSpec.getMode(heightMeasureSpec)
+ val specSize: Int = MeasureSpec.getSize(heightMeasureSpec)
+ if (specMode == MeasureSpec.EXACTLY) {
+ // 精度模式
+ height = specSize
+ } else {
+ // 默认大小
+ height = 200
+ // wrap_content
+ if (specMode == MeasureSpec.AT_MOST) {
+ height = height.coerceAtMost(specSize)
+ }
+ }
+ return height
+ }
+
+ private fun isDisabled(): Boolean {
+ return isDisabled
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ // 当View大小变化时,获取其宽高
+ mWidth = width
+ mHeight = height
+ circleX = mWidth / 2
+ circleY = mWidth / 2
+ radius = mWidth / 2
+ // 设置默认状态
+ state = STATE_UNDONE
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ drawOutSideCircle(canvas)
+ if (STATE_DONE == state) {
+ drawInternelRectangle(canvas)
+ } else {
+ if (isTouched) {
+ drawInternalCircle(canvas, insideCircleTouchedBgColor)
+ } else {
+ drawInternalCircle(canvas, insideCircleBgColor)
+ }
+ // 绘制弧形进度条
+ if (STATE_DOING == state) {
+ drawProgressArc(canvas)
+ }
+ }
+ if (isRecordVideo) {
+ drawRecordVideoCircle(canvas)
+ }
+ }
+
+ private fun drawRecordVideoCircle(canvas: Canvas) {
+ mPaint?.strokeWidth = 2F
+ mPaint?.style = Paint.Style.FILL
+ mPaint?.color = resources.getColor(R.color.colorRed)
+ mPaint?.isAntiAlias = true
+ canvas.drawCircle(
+ circleX.toFloat(),
+ circleY.toFloat(), (radius - radius * 0.75).toFloat(), mPaint!!
+ )
+ }
+
+ private fun drawOutSideCircle(canvas: Canvas) {
+ mPaint?.strokeWidth = 2.5F
+ mPaint?.color = outsideCircleBgColor
+ mPaint?.style = Paint.Style.STROKE
+ mPaint?.isAntiAlias = true
+ canvas.drawColor(Color.TRANSPARENT)
+ canvas.drawCircle(circleX.toFloat(), circleY.toFloat(), radius.toFloat() - 5F, mPaint!!)
+ }
+
+ private fun drawInternalCircle(canvas: Canvas, colorType: Int) {
+ mPaint?.strokeWidth = 2F
+ mPaint?.style = Paint.Style.FILL
+ mPaint?.color = colorType
+ mPaint?.isAntiAlias = true
+ canvas.drawCircle(
+ circleX.toFloat(),
+ circleY.toFloat(), (radius - radius * 0.35).toFloat(), mPaint!!
+ )
+ }
+
+ private fun drawInternelRectangle(canvas: Canvas) {
+ mPaint?.strokeWidth = 2F
+ mPaint?.color = insideRectangleBgColor
+ mPaint?.isAntiAlias = true
+ mPaint?.style = Paint.Style.FILL
+ canvas.drawRect(
+ (mWidth * 0.3).toFloat(),
+ (mWidth * 0.3).toFloat(),
+ (mWidth - mWidth * 0.3).toFloat(),
+ (mWidth - mWidth * 0.3).toFloat(),
+ mPaint!!
+ )
+ }
+
+ private fun drawProgressArc(canvas: Canvas) {
+ mPaint?.strokeWidth = (radius * 0.15).toInt().toFloat()
+ mPaint?.style = Paint.Style.STROKE
+ mPaint?.isAntiAlias = true
+ mPaint?.color = progressArcBgColor
+ if (progress >= 0) {
+ if (totalSize == 0) return
+ canvas.drawArc(
+ RectF(
+ (radius * 0.08).toFloat(),
+ (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat()
+ ),
+ 180F,
+ ((DecimalFormat("0.00")
+ .format(progress.toFloat() / totalSize).toFloat() * 360).toInt()).toFloat(),
+ false,
+ mPaint!!
+ )
+ if (isShowTextTip) {
+ drawTextTip(
+ canvas,
+ (DecimalFormat("0.00")
+ .format(progress.toFloat() / totalSize).toFloat() * 100).toString() + " %"
+ )
+ }
+ } else if (progress == NONE) {
+ if (isOddNumber) {
+ canvas.drawArc(
+ RectF(
+ (radius * 0.08).toFloat(),
+ (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat()
+ ), 180F, mSweepAngle.toFloat(), false, mPaint!!
+ )
+ mSweepAngle++
+ if (mSweepAngle >= 360) isOddNumber = false
+ } else {
+ canvas.drawArc(
+ RectF(
+ (radius * 0.08).toFloat(),
+ (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat(),
+ 2 * radius - (radius * 0.08).toFloat()
+ ), 180F, (-mSweepAngle).toFloat(), false, mPaint!!
+ )
+ mSweepAngle--
+ if (mSweepAngle == 0) isOddNumber = true
+ }
+ this.postInvalidateDelayed(5)
+ }
+ }
+
+ private fun drawTextTip(canvas: Canvas, tipText: String) {
+ mPaint?.strokeWidth = 2F
+ mPaint?.style = Paint.Style.FILL
+ mPaint?.isAntiAlias = true
+ mPaint?.textSize = tipTextSize
+ mPaint?.color = tipTextColor
+ //Paint.Align.CENTER , x表示字体中心位置;
+ // Paint.Align.LEFT ,x表示文本左边位置;
+ mPaint?.textAlign = Paint.Align.CENTER
+ val xCenter: Int = measuredHeight / 2
+ val yBaseLine: Float =
+ ((measuredHeight - mPaint?.fontMetrics!!.bottom + mPaint?.fontMetrics!!.top) / 2
+ - mPaint?.fontMetrics!!.top)
+ canvas.drawText(tipText, xCenter.toFloat(), yBaseLine, mPaint!!)
+ }
+
+ fun setMode(model: Int) {
+ isRecordVideo = model == MODEL_VIDEO
+ this.invalidate()
+ }
+
+ companion object {
+ // 状态正在进行
+ private const val STATE_DOING = 0
+ // 状态操作完成
+ private const val STATE_DONE = 1
+ // 状态操作未完成或初始状态
+ private const val STATE_UNDONE = 2
+ private const val NONE = -1
+
+ private const val MODEL_PICTURE = 0
+ private const val MODEL_VIDEO = 1
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/IAspectRatio.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/IAspectRatio.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ba1b8b7ceb6e6ef276139416fb1b86ea283c08e6
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/IAspectRatio.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.view.Surface
+
+/** aspect ratio setting func interface
+ *
+ * @author Created by jiangdg on 2022/1/26
+ */
+interface IAspectRatio {
+ fun setAspectRatio(width: Int, height: Int)
+ fun getSurfaceWidth(): Int
+ fun getSurfaceHeight(): Int
+ fun getSurface(): Surface?
+ fun postUITask(task: ()->Unit)
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/PreviewImageView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/PreviewImageView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7d4fc8e690f5515d4b453c99667e0bad611af076
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/PreviewImageView.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.animation.*
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.util.AttributeSet
+import android.view.animation.LinearInterpolator
+import androidx.annotation.RequiresApi
+import androidx.appcompat.widget.AppCompatImageView
+import com.jiangdg.ausbc.utils.Logger
+
+/**
+ * author: jiangdg
+ * date: 2020/11/4 3:16 PM
+ * description: Preview ImageView
+ */
+class PreviewImageView: AppCompatImageView {
+
+ private var isNewImageLoading: Boolean = false
+ private var mProgressAnim: ValueAnimator? = null
+ private lateinit var mProgressPath: Path
+ private lateinit var mProgressPathMeasure: PathMeasure
+ private var mProgressDstPath: Path? = null
+
+ private var mBreathAnimation: ObjectAnimator? = null
+ private val mPaint = Paint()
+ private var progress = 0f
+ private var mListener: OnLoadingFinishListener? = null
+ private var mTheme = Theme.LIGHT
+
+ private val mSrcRadii = FloatArray(8)
+ private val mBorderRadii = FloatArray(8)
+
+ private val mBorderRectF: RectF = RectF()
+ private val mSrcRectF: RectF = RectF()
+ private var mTmpPath: Path = Path()
+ private val mClipPath: Path = Path()
+ private lateinit var mXfermode: PorterDuffXfermode
+ private val cornerRadius = dp2px(5f)
+ private val borderWidth = dp2px(1f)
+
+ var canShowImageBorder = false
+ set(value) {
+ field = value
+ postInvalidate()
+ }
+
+ enum class Theme {
+ LIGHT, DARK
+ }
+
+ constructor(context: Context?) : this(context, null)
+ constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context!!,
+ attrs,
+ defStyleAttr
+ ) {
+ init()
+ }
+
+ interface OnLoadingFinishListener {
+ fun onLoadingFinish()
+ }
+
+ private fun init() {
+ // 设置PorterDuff模式
+ mXfermode = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
+ PorterDuffXfermode(PorterDuff.Mode.DST_IN)
+ } else {
+ PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
+ }
+ // 计算圆角半径
+ for (i in mBorderRadii.indices) {
+ mBorderRadii[i] = cornerRadius
+ mSrcRadii[i] = cornerRadius - borderWidth / 2.0f
+ }
+ }
+
+ private fun initBorderPath(w: Int, h: Int) {
+ // 计算外边框的RectF
+ mBorderRectF.set(borderWidth/2.0f, borderWidth/2.0f, w - borderWidth / 2.0f, h - borderWidth / 2.0f)
+ // 计算图片原始区域的RectF
+ mSrcRectF.set(0.0f, 0.0f, w.toFloat(), h.toFloat())
+ }
+
+ private fun initProgressPath(w: Int, h: Int) {
+ val radius = cornerRadius // 圆角半径
+ val p = borderWidth / 2.0f // 边距
+ val rectFLeftTop = RectF(p, p, (p + 2 * radius), (p + 2 * radius))
+ val rectFRightTop = RectF((w - p - 2 * radius), p, (w - p), (2 * radius + p))
+ val rectFLeftBottom = RectF(p, (h - p - 2 * radius), (p + 2 * radius), (h - p))
+ val rectFRightBottom = RectF((w - p - 2 * radius), (h - p - 2 * radius), (w - p), (h - p))
+ mProgressPath = Path().apply {
+ moveTo(w / 2f - p, p)
+ // 右上角
+ lineTo(w - radius - p, p)
+ arcTo(rectFRightTop, 270f, 90f)
+ // 右下角
+ lineTo(w - p, h - p - radius)
+ arcTo(rectFRightBottom, 0f, 90f)
+ // 左下角
+ lineTo(p + radius, h - p)
+ arcTo(rectFLeftBottom, 90f, 90f)
+ // 左上角
+ lineTo(p, p + radius)
+ arcTo(rectFLeftTop, 180f, 90f)
+ lineTo(w / 2f - p, p)
+ }
+ mProgressDstPath = Path()
+ mProgressPathMeasure = PathMeasure(mProgressPath, true)
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ initProgressPath(w, h)
+ initBorderPath(w, h)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun onDraw(canvas: Canvas?) {
+ try {
+ canvas?.saveLayer(mSrcRectF, null)
+ // 缩小画布
+// val sx = 1.0f * (width - borderWidth) / width
+// val sy = 1.0f * (height - borderWidth) / height
+// canvas?.scale(sx, sy, width / 2.0f, height / 2.0f)
+
+ // 绘制原图
+ super.onDraw(canvas)
+
+ // 裁剪,得到圆角原图
+ mPaint.reset()
+ mPaint.isAntiAlias = true
+ mPaint.style = Paint.Style.FILL
+ mPaint.color = Color.parseColor("#FFFFFF")
+ mPaint.xfermode = mXfermode
+ mClipPath.reset()
+ mClipPath.addRoundRect(mSrcRectF, mSrcRadii, Path.Direction.CCW)
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
+ canvas?.drawPath(mClipPath, mPaint)
+ } else {
+ mTmpPath.reset()
+ mTmpPath.addRect(mSrcRectF, Path.Direction.CCW)
+ mTmpPath.op(mClipPath, Path.Op.DIFFERENCE)
+ canvas?.drawPath(mTmpPath, mPaint)
+ }
+ mPaint.xfermode = null
+ canvas?.restore()
+
+ // 绘制边框
+ drawBorders(canvas)
+ // 绘制进度条
+ drawBorderProgress(canvas)
+ } catch (e: Exception) {
+ Logger.e(TAG, "draw preview image view failed", e)
+ e.printStackTrace()
+ }
+ }
+
+ private fun drawBorders(canvas: Canvas?) {
+ if (mTheme == Theme.LIGHT || !canShowImageBorder) return
+ mClipPath.reset()
+ mPaint.isAntiAlias = true
+ mPaint.strokeWidth = borderWidth
+ mPaint.color = Color.parseColor("#FFFFFF")
+ mPaint.style = Paint.Style.STROKE
+ mClipPath.addRoundRect(mBorderRectF, mBorderRadii, Path.Direction.CCW)
+ canvas?.drawPath(mClipPath, mPaint)
+ }
+
+ private fun drawBorderProgress(canvas: Canvas?) {
+ mPaint.reset()
+ mPaint.isAntiAlias = true
+ mPaint.strokeWidth = borderWidth
+ mPaint.style = Paint.Style.STROKE
+ mPaint.color = Color.parseColor("#2E5BFF")
+
+ mProgressDstPath?.let {
+ if (! it.isEmpty) {
+ canvas?.drawPath(it, mPaint)
+ }
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ cancelAnimation()
+ }
+
+ override fun setImageDrawable(drawable: Drawable?) {
+ super.setImageDrawable(drawable)
+ drawable?.let {
+ if (isAnimationRunning()) {
+ mProgressAnim?.cancel()
+ mProgressAnim = null
+ }
+ if (isNewImageLoading || getProgress() != 0.0f) {//解决setImageDrawable多次調用會闪动
+ if ((mBreathAnimation == null || mBreathAnimation?.isRunning == false)) {
+ showBreathAnimation()
+ }
+ }
+ }
+ }
+
+ fun cancelAnimation() {
+ mBreathAnimation?.cancel()
+ mBreathAnimation = null
+ mProgressAnim?.cancel()
+ mProgressAnim = null
+ setProgress(0.0f)
+ isNewImageLoading = false
+ mListener?.onLoadingFinish()
+ }
+
+ fun setNewImageFlag(isNewImage: Boolean) {
+ this.isNewImageLoading = isNewImage
+ }
+
+ fun showImageLoadProgress(isShowFakeProgress: Boolean = true) {
+ if (isAnimationRunning()) {
+ return
+ }
+ if (! isShowFakeProgress) {
+ showBreathAnimation()
+ return
+ }
+ // 假进度
+ mProgressAnim = ValueAnimator.ofFloat(0.0f, 0.7f)
+ mProgressAnim?.interpolator = LinearInterpolator()
+ mProgressAnim?.duration = 3000
+ mProgressAnim?.addUpdateListener {
+ val value = it.animatedValue
+ if (value is Float) {
+ setProgress(value)
+ }
+ }
+ mProgressAnim?.start()
+ }
+
+ fun setOnLoadingFinishListener(listener: OnLoadingFinishListener?) {
+ this.mListener = listener
+ }
+
+ fun setTheme(theme: Theme) {
+ this.mTheme = theme
+ }
+
+ private fun showBreathAnimation() {
+ if (isAnimationRunning()) {
+ return
+ }
+ val scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.2f, 1.0f)
+ val scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 1.2f, 1.0f)
+ val progress = PropertyValuesHolder.ofFloat("progress", getProgress(), 1.0f)
+ mBreathAnimation = ObjectAnimator.ofPropertyValuesHolder(this, scaleX, scaleY, progress).apply {
+ addListener(object :AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ super.onAnimationEnd(animation)
+ isNewImageLoading = false
+ setProgress(0.0f)
+ mBreathAnimation = null
+ mListener?.onLoadingFinish()
+ }
+ })
+ duration = 150
+ interpolator = LinearInterpolator()
+ start()
+ }
+ }
+
+ private fun isAnimationRunning() = mProgressAnim?.isRunning ==true || mBreathAnimation?.isRunning == true
+
+ private fun setProgress(progress: Float) {
+ this.progress = progress
+ updateBorderProgress()
+ }
+
+ private fun getProgress(): Float {
+ return progress
+ }
+
+ private fun updateBorderProgress() {
+ mProgressDstPath?.let {
+ it.reset()
+ mProgressPathMeasure.getSegment(0f,
+ mProgressPathMeasure.length * progress, it, true)
+ invalidate()
+ }
+ }
+
+ private fun dp2px(dpValue: Float): Float {
+ val scale = resources.displayMetrics.density
+ return dpValue * scale + 0.5f
+ }
+
+ companion object {
+ private const val TAG = "PreviewImageView"
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/java/com/jiangdg/ausbc/widget/TipView.kt b/libausbc/src/main/java/com/jiangdg/ausbc/widget/TipView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b7926f240a1c0d166afc6e48efa571ca53b854da
--- /dev/null
+++ b/libausbc/src/main/java/com/jiangdg/ausbc/widget/TipView.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.ausbc.widget
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import android.util.AttributeSet
+import android.view.View
+import androidx.appcompat.widget.AppCompatTextView
+
+class TipView : AppCompatTextView {
+
+ private val mHandler = Handler(Looper.getMainLooper())
+
+ private var showAnim = ObjectAnimator.ofFloat(this, "alpha", 0.08f, 0.8f)
+ private var hideAnim = ObjectAnimator.ofFloat(this, "alpha", 0.8f, 0f)
+
+ private val mGoneRunnable = Runnable {
+ hideAnim.start()
+ }
+
+ constructor(context: Context?) : this(context, null)
+ constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context!!,
+ attrs,
+ defStyleAttr
+ ) {
+ init(context)
+ }
+
+ private fun init(context: Context?) {
+ hideAnim.addListener(object : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animation: Animator?) {
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ if (this@TipView.visibility == View.VISIBLE) {
+ this@TipView.visibility = View.GONE
+ }
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ }
+
+ override fun onAnimationStart(animation: Animator?) {
+ }
+ })
+ }
+
+ fun show(name: String, duration: Long = SHOW_DURATION) {
+ if (hideAnim.isRunning || hideAnim.isStarted) {
+ hideAnim.cancel()
+ }
+
+ text = name
+ if (this@TipView.visibility != View.VISIBLE) {
+ this@TipView.visibility = View.VISIBLE
+ }
+
+ if (showAnim.isPaused || !showAnim.isRunning || !showAnim.isStarted) {
+ showAnim.start()
+ }
+
+ mHandler.removeCallbacksAndMessages(null)
+ mHandler.postDelayed(mGoneRunnable, duration)
+ }
+
+ companion object {
+ const val SHOW_DURATION = 1500L
+ }
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/drawable/effect_none.xml b/libausbc/src/main/res/drawable/effect_none.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f2d9c646b94f480217c5652d9e3579e32688ebef
--- /dev/null
+++ b/libausbc/src/main/res/drawable/effect_none.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/libausbc/src/main/res/drawable/ic_none.png b/libausbc/src/main/res/drawable/ic_none.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab54440f9cb80f3f4955b5da0208cfaadf48170a
Binary files /dev/null and b/libausbc/src/main/res/drawable/ic_none.png differ
diff --git a/libausbc/src/main/res/drawable/imageloader_default_cover_bg.xml b/libausbc/src/main/res/drawable/imageloader_default_cover_bg.xml
new file mode 100644
index 0000000000000000000000000000000000000000..db7b2cc5d66b650da66f5ca7cb4801dd1d12f71c
--- /dev/null
+++ b/libausbc/src/main/res/drawable/imageloader_default_cover_bg.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/base_fragment.glsl b/libausbc/src/main/res/raw/base_fragment.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..9270a3cb9cef8be51f9534cf81ec1558a21db934
--- /dev/null
+++ b/libausbc/src/main/res/raw/base_fragment.glsl
@@ -0,0 +1,7 @@
+precision highp float;
+uniform sampler2D uTextureSampler;
+varying vec2 vTextureCoord;
+void main()
+{
+ gl_FragColor = texture2D(uTextureSampler, vTextureCoord);
+}
diff --git a/libausbc/src/main/res/raw/base_vertex.glsl b/libausbc/src/main/res/raw/base_vertex.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..c39c879ad677a0569b823f399286f04948f015e3
--- /dev/null
+++ b/libausbc/src/main/res/raw/base_vertex.glsl
@@ -0,0 +1,8 @@
+attribute vec4 aPosition;
+attribute vec4 aTextureCoordinate;
+varying vec2 vTextureCoord;
+void main()
+{
+ vTextureCoord = aTextureCoordinate.xy;
+ gl_Position = aPosition;
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/camera_fragment.glsl b/libausbc/src/main/res/raw/camera_fragment.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..a4661c7cd2ed54b53512d360fbaee49deff3d4a0
--- /dev/null
+++ b/libausbc/src/main/res/raw/camera_fragment.glsl
@@ -0,0 +1,8 @@
+#extension GL_OES_EGL_image_external : require
+precision mediump float;
+uniform samplerExternalOES uTextureSampler;
+varying vec2 vTextureCoord;
+void main()
+{
+ gl_FragColor = texture2D(uTextureSampler, vTextureCoord);
+}
diff --git a/libausbc/src/main/res/raw/camera_vertex.glsl b/libausbc/src/main/res/raw/camera_vertex.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..764cc68d373254e44c2c52b0808c060a29548716
--- /dev/null
+++ b/libausbc/src/main/res/raw/camera_vertex.glsl
@@ -0,0 +1,10 @@
+uniform mat4 uMVPMatrix;
+attribute vec4 aPosition;
+uniform mat4 uStMatrix;
+attribute vec4 aTextureCoordinate;
+varying vec2 vTextureCoord;
+void main()
+{
+ gl_Position = uMVPMatrix * aPosition;
+ vTextureCoord = (uStMatrix * aTextureCoordinate).xy;
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/capture_vertex.glsl b/libausbc/src/main/res/raw/capture_vertex.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..e689d8aada707db42546da7d3fb570f98d1f05b4
--- /dev/null
+++ b/libausbc/src/main/res/raw/capture_vertex.glsl
@@ -0,0 +1,9 @@
+uniform mat4 uMVPMatrix;
+attribute vec4 aPosition;
+attribute vec4 aTextureCoordinate;
+varying vec2 vTextureCoord;
+void main()
+{
+ gl_Position = uMVPMatrix * aPosition;
+ vTextureCoord = aTextureCoordinate.xy;
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/effect_blackw_fragment.glsl b/libausbc/src/main/res/raw/effect_blackw_fragment.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..692a81b0d74ace65fbde8da13769911303a90c4e
--- /dev/null
+++ b/libausbc/src/main/res/raw/effect_blackw_fragment.glsl
@@ -0,0 +1,10 @@
+precision mediump float;
+uniform sampler2D uTextureSampler;
+varying vec2 vTextureCoord;
+void main()
+{
+ vec4 tempColor = texture2D(uTextureSampler, vTextureCoord);
+ // Get the grayscale value of each pixel
+ float luminance = tempColor.r * 0.299 + tempColor.g * 0.584 + tempColor.b * 0.114;
+ gl_FragColor = vec4(vec3(luminance), tempColor.a);
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/effect_soul_fragment.glsl b/libausbc/src/main/res/raw/effect_soul_fragment.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..4fbd1ed75f4f932eae559c3cc660ad50e604e4b7
--- /dev/null
+++ b/libausbc/src/main/res/raw/effect_soul_fragment.glsl
@@ -0,0 +1,35 @@
+precision mediump float;
+
+uniform sampler2D uTextureSampler;
+varying vec2 vTextureCoord;
+//timestamp
+uniform float timeStamps;
+
+void main() {
+ //The duration of an out-of-body effect
+ float duration = 0.7;
+ // upper limit of transparency
+ float maxAlpha = 0.4;
+ //The upper limit of image enlargement
+ float maxScale = 1.8;
+
+ //The current progress (timestamp and duration are modulo modulo), then divide by the duration to get [0, 1], which is the percentage
+ float progress = mod(timeStamps, duration) / duration; // 0~1
+ //Current transparency [0.4, 0]
+ float alpha = maxAlpha * (1.0 - progress);
+ //Current zoom ratio [1.0, 1.8]
+ float scale = 1.0 + (maxScale - 1.0) * progress;
+
+ //Get the enlarged texture coordinates
+ //Reduce the distance from the x/y value of the texture coordinate corresponding to the vertex coordinate to the center point, reduce a certain ratio, just change the texture coordinate, and keep the vertex coordinate unchanged, so as to achieve the stretching effect
+ float weakX = 0.5 + (vTextureCoord.x - 0.5) / scale;
+ float weakY = 0.5 + (vTextureCoord.y - 0.5) / scale;
+ vec2 weakTextureCoords = vec2(weakX, weakY);
+
+ //Get the texture coordinates of the current pixel, the enlarged texture coordinates
+ vec4 weakMask = texture2D(uTextureSampler, weakTextureCoords);
+
+ vec4 mask = texture2D(uTextureSampler, vTextureCoord);
+ //2, color mixing built-in function mix / mixing equation
+ gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/raw/effect_zoom_vertex.glsl b/libausbc/src/main/res/raw/effect_zoom_vertex.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..8549941b36f01c542c892f3496f1445b82ea9e58
--- /dev/null
+++ b/libausbc/src/main/res/raw/effect_zoom_vertex.glsl
@@ -0,0 +1,17 @@
+attribute vec4 aPosition;
+attribute vec4 aTextureCoordinate;
+varying vec2 vTextureCoord;
+uniform float timeStamps;
+const float PI = 3.1415926;
+
+void main()
+{
+ float duration = 0.9; // zoom period, 0.9s
+ float maxAmplitude = 0.3; // scale peak offset
+ float modTime = mod(timeStamps, duration);
+ // zoom range [1.0f, 1.3f]
+ float amplitude = 1.0 + maxAmplitude * abs(sin((modTime / duration) * PI));
+
+ gl_Position = vec4(aPosition.x * amplitude, aPosition.y * amplitude, aPosition.zw);
+ vTextureCoord = aTextureCoordinate.xy;
+}
\ No newline at end of file
diff --git a/libausbc/src/main/res/values/attr.xml b/libausbc/src/main/res/values/attr.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ab54732097d7e10a436ec63ec5a812cb7c22c866
--- /dev/null
+++ b/libausbc/src/main/res/values/attr.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libausbc/src/main/res/values/colors.xml b/libausbc/src/main/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a7fb7dd29e3be75f3961ac667ebbdf0eb13f556d
--- /dev/null
+++ b/libausbc/src/main/res/values/colors.xml
@@ -0,0 +1,40 @@
+
+
+ #B22222
+ #FF0000
+ #000000
+ #FFFFFF
+ #CDC5BF
+
+ #2E5BFF
+ #2E5BFF
+ #2E5BFF
+ #FFFFFFFF
+ #232325
+ #232325
+ #F2F2F2
+ #0c000000
+ #2E5BFF
+ #D7DAE1
+ #70000000
+ #232325
+ #ADB0B7
+ #F6F7FB
+ #E9ECF3
+ #50607A
+ #FFFFFF
+ #FF6835
+ #f2f5f7
+ #88757575
+ #D7DAE1
+ #90FFFFFF
+ #000000
+ #A8000000
+ #4D000000
+ #40000000
+ #FF371C
+ #FFE5E5E5
+ #F2F4F6
+ #F5F7FB
+ #13000000
+
\ No newline at end of file
diff --git a/libausbc/src/main/res/values/styles.xml b/libausbc/src/main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bf153bdc66809f3b7e1fbe363f60ab4a8718a47e
--- /dev/null
+++ b/libausbc/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libausbc/src/main/res/xml/default_device_filter.xml b/libausbc/src/main/res/xml/default_device_filter.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a28549ddf5f4b263bb03796cf61b79d31fbb4d62
--- /dev/null
+++ b/libausbc/src/main/res/xml/default_device_filter.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libnative/.gitignore b/libnative/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8
--- /dev/null
+++ b/libnative/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/libnative/build.gradle b/libnative/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..ce9c550249af411e20e2f04f6a2489455c2ca0a4
--- /dev/null
+++ b/libnative/build.gradle
@@ -0,0 +1,56 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ }
+ ndk{
+ abiFilters 'armeabi-v7a','arm64-v8a'
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.10.2"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+}
\ No newline at end of file
diff --git a/libnative/consumer-rules.pro b/libnative/consumer-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libnative/proguard-rules.pro b/libnative/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/libnative/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/libnative/src/androidTest/java/com/jiangdg/natives/ExampleInstrumentedTest.kt b/libnative/src/androidTest/java/com/jiangdg/natives/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f7338ad246f8137ace94a8d472ebce2a0be54624
--- /dev/null
+++ b/libnative/src/androidTest/java/com/jiangdg/natives/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.jiangdg.natives
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.jiangdg.nativelib.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/libnative/src/main/AndroidManifest.xml b/libnative/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b64242d21cd13447f93b6c74ac3f1746c789fcd1
--- /dev/null
+++ b/libnative/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/libnative/src/main/cpp/CMakeLists.txt b/libnative/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8a1f0e26a6de08d95155b1ade5087a2df7e86fca
--- /dev/null
+++ b/libnative/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,29 @@
+
+cmake_minimum_required(VERSION 3.10.2)
+
+
+project("nativelib")
+
+# lame
+include_directories(${CMAKE_SOURCE_DIR}/module/mp3/lame)
+aux_source_directory(${CMAKE_SOURCE_DIR}/module/mp3/lame SRC_LAME)
+
+add_library(
+ nativelib
+ SHARED
+
+ ${SRC_LAME}
+ utils/logger.cpp
+ module/yuv/yuv.cpp
+ module/mp3/mp3.cpp
+ proxy/proxy_yuv.cpp
+ proxy/proxy_mp3.cpp
+ nativelib.cpp)
+
+find_library(
+ log-lib
+ log)
+
+target_link_libraries(
+ nativelib
+ ${log-lib})
\ No newline at end of file
diff --git a/libnative/src/main/cpp/module/mp3/lame/VbrTag.c b/libnative/src/main/cpp/module/mp3/lame/VbrTag.c
new file mode 100644
index 0000000000000000000000000000000000000000..677868b52a935ff2d8db44d70d4049f79bddf14b
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/VbrTag.c
@@ -0,0 +1,1083 @@
+/*
+ * Xing VBR tagging for LAME.
+ *
+ * Copyright (c) 1999 A.L. Faber
+ * Copyright (c) 2001 Jonathan Dee
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: VbrTag.c,v 1.103.2.1 2011/11/18 09:18:28 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "bitstream.h"
+#include "VbrTag.h"
+#include "lame_global_flags.h"
+#include "tables.h"
+
+#ifdef __sun__
+/* woraround for SunOS 4.x, it has SEEK_* defined here */
+#include
+#endif
+
+
+#ifdef _DEBUG
+/* #define DEBUG_VBRTAG */
+#endif
+
+/*
+ * 4 bytes for Header Tag
+ * 4 bytes for Header Flags
+ * 100 bytes for entry (NUMTOCENTRIES)
+ * 4 bytes for FRAME SIZE
+ * 4 bytes for STREAM_SIZE
+ * 4 bytes for VBR SCALE. a VBR quality indicator: 0=best 100=worst
+ * 20 bytes for LAME tag. for example, "LAME3.12 (beta 6)"
+ * ___________
+ * 140 bytes
+*/
+#define VBRHEADERSIZE (NUMTOCENTRIES+4+4+4+4+4)
+
+#define LAMEHEADERSIZE (VBRHEADERSIZE + 9 + 1 + 1 + 8 + 1 + 1 + 3 + 1 + 1 + 2 + 4 + 2 + 2)
+
+/* the size of the Xing header (MPEG1 and MPEG2) in kbps */
+#define XING_BITRATE1 128
+#define XING_BITRATE2 64
+#define XING_BITRATE25 32
+
+extern const char* get_lame_tag_encoder_short_version(void);
+
+static const char VBRTag0[] = { "Xing" };
+static const char VBRTag1[] = { "Info" };
+
+
+
+
+/* Lookup table for fast CRC computation
+ * See 'CRC_update_lookup'
+ * Uses the polynomial x^16+x^15+x^2+1 */
+
+static const unsigned int crc16_lookup[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+
+
+
+
+/***********************************************************************
+ * Robert Hegemann 2001-01-17
+ ***********************************************************************/
+
+static void
+addVbr(VBR_seek_info_t * v, int bitrate)
+{
+ int i;
+
+ v->nVbrNumFrames++;
+ v->sum += bitrate;
+ v->seen++;
+
+ if (v->seen < v->want) {
+ return;
+ }
+
+ if (v->pos < v->size) {
+ v->bag[v->pos] = v->sum;
+ v->pos++;
+ v->seen = 0;
+ }
+ if (v->pos == v->size) {
+ for (i = 1; i < v->size; i += 2) {
+ v->bag[i / 2] = v->bag[i];
+ }
+ v->want *= 2;
+ v->pos /= 2;
+ }
+}
+
+static void
+Xing_seek_table(VBR_seek_info_t const* v, unsigned char *t)
+{
+ int i, indx;
+ int seek_point;
+
+ if (v->pos <= 0)
+ return;
+
+ for (i = 1; i < NUMTOCENTRIES; ++i) {
+ float j = i / (float) NUMTOCENTRIES, act, sum;
+ indx = (int) (floor(j * v->pos));
+ if (indx > v->pos - 1)
+ indx = v->pos - 1;
+ act = v->bag[indx];
+ sum = v->sum;
+ seek_point = (int) (256. * act / sum);
+ if (seek_point > 255)
+ seek_point = 255;
+ t[i] = seek_point;
+ }
+}
+
+#ifdef DEBUG_VBR_SEEKING_TABLE
+static void
+print_seeking(unsigned char *t)
+{
+ int i;
+
+ printf("seeking table ");
+ for (i = 0; i < NUMTOCENTRIES; ++i) {
+ printf(" %d ", t[i]);
+ }
+ printf("\n");
+}
+#endif
+
+
+/****************************************************************************
+ * AddVbrFrame: Add VBR entry, used to fill the VBR the TOC entries
+ * Paramters:
+ * nStreamPos: how many bytes did we write to the bitstream so far
+ * (in Bytes NOT Bits)
+ ****************************************************************************
+*/
+void
+AddVbrFrame(lame_internal_flags * gfc)
+{
+ int kbps = bitrate_table[gfc->cfg.version][gfc->ov_enc.bitrate_index];
+ assert(gfc->VBR_seek_table.bag);
+ addVbr(&gfc->VBR_seek_table, kbps);
+}
+
+
+/*-------------------------------------------------------------*/
+static int
+ExtractI4(const unsigned char *buf)
+{
+ int x;
+ /* big endian extract */
+ x = buf[0];
+ x <<= 8;
+ x |= buf[1];
+ x <<= 8;
+ x |= buf[2];
+ x <<= 8;
+ x |= buf[3];
+ return x;
+}
+
+static void
+CreateI4(unsigned char *buf, uint32_t nValue)
+{
+ /* big endian create */
+ buf[0] = (nValue >> 24) & 0xff;
+ buf[1] = (nValue >> 16) & 0xff;
+ buf[2] = (nValue >> 8) & 0xff;
+ buf[3] = (nValue) & 0xff;
+}
+
+
+
+static void
+CreateI2(unsigned char *buf, int nValue)
+{
+ /* big endian create */
+ buf[0] = (nValue >> 8) & 0xff;
+ buf[1] = (nValue) & 0xff;
+}
+
+/* check for magic strings*/
+static int
+IsVbrTag(const unsigned char *buf)
+{
+ int isTag0, isTag1;
+
+ isTag0 = ((buf[0] == VBRTag0[0]) && (buf[1] == VBRTag0[1]) && (buf[2] == VBRTag0[2])
+ && (buf[3] == VBRTag0[3]));
+ isTag1 = ((buf[0] == VBRTag1[0]) && (buf[1] == VBRTag1[1]) && (buf[2] == VBRTag1[2])
+ && (buf[3] == VBRTag1[3]));
+
+ return (isTag0 || isTag1);
+}
+
+#define SHIFT_IN_BITS_VALUE(x,n,v) ( x = (x << (n)) | ( (v) & ~(-1 << (n)) ) )
+
+static void
+setLameTagFrameHeader(lame_internal_flags const *gfc, unsigned char *buffer)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ char abyte, bbyte;
+
+ SHIFT_IN_BITS_VALUE(buffer[0], 8u, 0xffu);
+
+ SHIFT_IN_BITS_VALUE(buffer[1], 3u, 7);
+ SHIFT_IN_BITS_VALUE(buffer[1], 1u, (cfg->samplerate_out < 16000) ? 0 : 1);
+ SHIFT_IN_BITS_VALUE(buffer[1], 1u, cfg->version);
+ SHIFT_IN_BITS_VALUE(buffer[1], 2u, 4 - 3);
+ SHIFT_IN_BITS_VALUE(buffer[1], 1u, (!cfg->error_protection) ? 1 : 0);
+
+ SHIFT_IN_BITS_VALUE(buffer[2], 4u, eov->bitrate_index);
+ SHIFT_IN_BITS_VALUE(buffer[2], 2u, cfg->samplerate_index);
+ SHIFT_IN_BITS_VALUE(buffer[2], 1u, 0);
+ SHIFT_IN_BITS_VALUE(buffer[2], 1u, cfg->extension);
+
+ SHIFT_IN_BITS_VALUE(buffer[3], 2u, cfg->mode);
+ SHIFT_IN_BITS_VALUE(buffer[3], 2u, eov->mode_ext);
+ SHIFT_IN_BITS_VALUE(buffer[3], 1u, cfg->copyright);
+ SHIFT_IN_BITS_VALUE(buffer[3], 1u, cfg->original);
+ SHIFT_IN_BITS_VALUE(buffer[3], 2u, cfg->emphasis);
+
+ /* the default VBR header. 48 kbps layer III, no padding, no crc */
+ /* but sampling freq, mode andy copyright/copy protection taken */
+ /* from first valid frame */
+ buffer[0] = (uint8_t) 0xff;
+ abyte = (buffer[1] & (unsigned char) 0xf1);
+ {
+ int bitrate;
+ if (1 == cfg->version) {
+ bitrate = XING_BITRATE1;
+ }
+ else {
+ if (cfg->samplerate_out < 16000)
+ bitrate = XING_BITRATE25;
+ else
+ bitrate = XING_BITRATE2;
+ }
+
+ if (cfg->vbr == vbr_off)
+ bitrate = cfg->avg_bitrate;
+
+ if (cfg->free_format)
+ bbyte = 0x00;
+ else
+ bbyte = 16 * BitrateIndex(bitrate, cfg->version, cfg->samplerate_out);
+ }
+
+ /* Use as much of the info from the real frames in the
+ * Xing header: samplerate, channels, crc, etc...
+ */
+ if (cfg->version == 1) {
+ /* MPEG1 */
+ buffer[1] = abyte | (char) 0x0a; /* was 0x0b; */
+ abyte = buffer[2] & (char) 0x0d; /* AF keep also private bit */
+ buffer[2] = (char) bbyte | abyte; /* 64kbs MPEG1 frame */
+ }
+ else {
+ /* MPEG2 */
+ buffer[1] = abyte | (char) 0x02; /* was 0x03; */
+ abyte = buffer[2] & (char) 0x0d; /* AF keep also private bit */
+ buffer[2] = (char) bbyte | abyte; /* 64kbs MPEG2 frame */
+ }
+}
+
+#if 0
+static int CheckVbrTag(unsigned char *buf);
+
+/*-------------------------------------------------------------*/
+/* Same as GetVbrTag below, but only checks for the Xing tag.
+ requires buf to contain only 40 bytes */
+/*-------------------------------------------------------------*/
+int
+CheckVbrTag(unsigned char *buf)
+{
+ int h_id, h_mode;
+
+ /* get selected MPEG header data */
+ h_id = (buf[1] >> 3) & 1;
+ h_mode = (buf[3] >> 6) & 3;
+
+ /* determine offset of header */
+ if (h_id) {
+ /* mpeg1 */
+ if (h_mode != 3)
+ buf += (32 + 4);
+ else
+ buf += (17 + 4);
+ }
+ else {
+ /* mpeg2 */
+ if (h_mode != 3)
+ buf += (17 + 4);
+ else
+ buf += (9 + 4);
+ }
+
+ return IsVbrTag(buf);
+}
+#endif
+
+int
+GetVbrTag(VBRTAGDATA * pTagData, const unsigned char *buf)
+{
+ int i, head_flags;
+ int h_bitrate, h_id, h_mode, h_sr_index, h_layer;
+ int enc_delay, enc_padding;
+
+ /* get Vbr header data */
+ pTagData->flags = 0;
+
+ /* get selected MPEG header data */
+ h_layer = (buf[1] >> 1) & 3;
+ if ( h_layer != 0x01 ) {
+ /* the following code assumes Layer-3, so give up here */
+ return 0;
+ }
+ h_id = (buf[1] >> 3) & 1;
+ h_sr_index = (buf[2] >> 2) & 3;
+ h_mode = (buf[3] >> 6) & 3;
+ h_bitrate = ((buf[2] >> 4) & 0xf);
+ h_bitrate = bitrate_table[h_id][h_bitrate];
+
+ /* check for FFE syncword */
+ if ((buf[1] >> 4) == 0xE)
+ pTagData->samprate = samplerate_table[2][h_sr_index];
+ else
+ pTagData->samprate = samplerate_table[h_id][h_sr_index];
+ /* if( h_id == 0 ) */
+ /* pTagData->samprate >>= 1; */
+
+
+
+ /* determine offset of header */
+ if (h_id) {
+ /* mpeg1 */
+ if (h_mode != 3)
+ buf += (32 + 4);
+ else
+ buf += (17 + 4);
+ }
+ else {
+ /* mpeg2 */
+ if (h_mode != 3)
+ buf += (17 + 4);
+ else
+ buf += (9 + 4);
+ }
+
+ if (!IsVbrTag(buf))
+ return 0;
+
+ buf += 4;
+
+ pTagData->h_id = h_id;
+
+ head_flags = pTagData->flags = ExtractI4(buf);
+ buf += 4; /* get flags */
+
+ if (head_flags & FRAMES_FLAG) {
+ pTagData->frames = ExtractI4(buf);
+ buf += 4;
+ }
+
+ if (head_flags & BYTES_FLAG) {
+ pTagData->bytes = ExtractI4(buf);
+ buf += 4;
+ }
+
+ if (head_flags & TOC_FLAG) {
+ if (pTagData->toc != NULL) {
+ for (i = 0; i < NUMTOCENTRIES; i++)
+ pTagData->toc[i] = buf[i];
+ }
+ buf += NUMTOCENTRIES;
+ }
+
+ pTagData->vbr_scale = -1;
+
+ if (head_flags & VBR_SCALE_FLAG) {
+ pTagData->vbr_scale = ExtractI4(buf);
+ buf += 4;
+ }
+
+ pTagData->headersize = ((h_id + 1) * 72000 * h_bitrate) / pTagData->samprate;
+
+ buf += 21;
+ enc_delay = buf[0] << 4;
+ enc_delay += buf[1] >> 4;
+ enc_padding = (buf[1] & 0x0F) << 8;
+ enc_padding += buf[2];
+ /* check for reasonable values (this may be an old Xing header, */
+ /* not a INFO tag) */
+ if (enc_delay < 0 || enc_delay > 3000)
+ enc_delay = -1;
+ if (enc_padding < 0 || enc_padding > 3000)
+ enc_padding = -1;
+
+ pTagData->enc_delay = enc_delay;
+ pTagData->enc_padding = enc_padding;
+
+#ifdef DEBUG_VBRTAG
+ fprintf(stderr, "\n\n********************* VBR TAG INFO *****************\n");
+ fprintf(stderr, "tag :%s\n", VBRTag);
+ fprintf(stderr, "head_flags :%d\n", head_flags);
+ fprintf(stderr, "bytes :%d\n", pTagData->bytes);
+ fprintf(stderr, "frames :%d\n", pTagData->frames);
+ fprintf(stderr, "VBR Scale :%d\n", pTagData->vbr_scale);
+ fprintf(stderr, "enc_delay = %i \n", enc_delay);
+ fprintf(stderr, "enc_padding= %i \n", enc_padding);
+ fprintf(stderr, "toc:\n");
+ if (pTagData->toc != NULL) {
+ for (i = 0; i < NUMTOCENTRIES; i++) {
+ if ((i % 10) == 0)
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %3d", (int) (pTagData->toc[i]));
+ }
+ }
+ fprintf(stderr, "\n***************** END OF VBR TAG INFO ***************\n");
+#endif
+ return 1; /* success */
+}
+
+
+/****************************************************************************
+ * InitVbrTag: Initializes the header, and write empty frame to stream
+ * Paramters:
+ * fpStream: pointer to output file stream
+ * nMode : Channel Mode: 0=STEREO 1=JS 2=DS 3=MONO
+ ****************************************************************************
+*/
+int
+InitVbrTag(lame_global_flags * gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int kbps_header;
+
+#define MAXFRAMESIZE 2880 /* or 0xB40, the max freeformat 640 32kHz framesize */
+
+ /*
+ * Xing VBR pretends to be a 48kbs layer III frame. (at 44.1kHz).
+ * (at 48kHz they use 56kbs since 48kbs frame not big enough for
+ * table of contents)
+ * let's always embed Xing header inside a 64kbs layer III frame.
+ * this gives us enough room for a LAME version string too.
+ * size determined by sampling frequency (MPEG1)
+ * 32kHz: 216 bytes@48kbs 288bytes@ 64kbs
+ * 44.1kHz: 156 bytes 208bytes@64kbs (+1 if padding = 1)
+ * 48kHz: 144 bytes 192
+ *
+ * MPEG 2 values are the same since the framesize and samplerate
+ * are each reduced by a factor of 2.
+ */
+
+
+ if (1 == cfg->version) {
+ kbps_header = XING_BITRATE1;
+ }
+ else {
+ if (cfg->samplerate_out < 16000)
+ kbps_header = XING_BITRATE25;
+ else
+ kbps_header = XING_BITRATE2;
+ }
+
+ if (cfg->vbr == vbr_off)
+ kbps_header = cfg->avg_bitrate;
+
+ /** make sure LAME Header fits into Frame
+ */
+ {
+ int total_frame_size = ((cfg->version + 1) * 72000 * kbps_header) / cfg->samplerate_out;
+ int header_size = (cfg->sideinfo_len + LAMEHEADERSIZE);
+ gfc->VBR_seek_table.TotalFrameSize = total_frame_size;
+ if (total_frame_size < header_size || total_frame_size > MAXFRAMESIZE) {
+ /* disable tag, it wont fit */
+ gfc->cfg.write_lame_tag = 0;
+ return 0;
+ }
+ }
+
+ gfc->VBR_seek_table.nVbrNumFrames = 0;
+ gfc->VBR_seek_table.nBytesWritten = 0;
+ gfc->VBR_seek_table.sum = 0;
+
+ gfc->VBR_seek_table.seen = 0;
+ gfc->VBR_seek_table.want = 1;
+ gfc->VBR_seek_table.pos = 0;
+
+ if (gfc->VBR_seek_table.bag == NULL) {
+ gfc->VBR_seek_table.bag = malloc(400 * sizeof(int));
+ if (gfc->VBR_seek_table.bag != NULL) {
+ gfc->VBR_seek_table.size = 400;
+ }
+ else {
+ gfc->VBR_seek_table.size = 0;
+ ERRORF(gfc, "Error: can't allocate VbrFrames buffer\n");
+ gfc->cfg.write_lame_tag = 0;
+ return -1;
+ }
+ }
+
+ /* write dummy VBR tag of all 0's into bitstream */
+ {
+ uint8_t buffer[MAXFRAMESIZE];
+ size_t i, n;
+
+ memset(buffer, 0, sizeof(buffer));
+ setLameTagFrameHeader(gfc, buffer);
+ n = gfc->VBR_seek_table.TotalFrameSize;
+ for (i = 0; i < n; ++i) {
+ add_dummy_byte(gfc, buffer[i], 1);
+ }
+ }
+ /* Success */
+ return 0;
+}
+
+
+
+/* fast CRC-16 computation - uses table crc16_lookup 8*/
+static uint16_t
+CRC_update_lookup(uint16_t value, uint16_t crc)
+{
+ uint16_t tmp;
+ tmp = crc ^ value;
+ crc = (crc >> 8) ^ crc16_lookup[tmp & 0xff];
+ return crc;
+}
+
+void
+UpdateMusicCRC(uint16_t * crc, unsigned char const *buffer, int size)
+{
+ int i;
+ for (i = 0; i < size; ++i)
+ *crc = CRC_update_lookup(buffer[i], *crc);
+}
+
+
+
+
+
+/****************************************************************************
+ * Jonathan Dee 2001/08/31
+ *
+ * PutLameVBR: Write LAME info: mini version + info on various switches used
+ * Paramters:
+ * pbtStreamBuffer : pointer to output buffer
+ * id3v2size : size of id3v2 tag in bytes
+ * crc : computation of crc-16 of Lame Tag so far (starting at frame sync)
+ *
+ ****************************************************************************
+*/
+static int
+PutLameVBR(lame_global_flags const *gfp, size_t nMusicLength, uint8_t * pbtStreamBuffer, uint16_t crc)
+{
+ lame_internal_flags const *gfc = gfp->internal_flags;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+
+ int nBytesWritten = 0;
+ int i;
+
+ int enc_delay = gfc->ov_enc.encoder_delay; /* encoder delay */
+ int enc_padding = gfc->ov_enc.encoder_padding; /* encoder padding */
+
+ /*recall: cfg->vbr_q is for example set by the switch -V */
+ /* gfp->quality by -q, -h, -f, etc */
+
+ int nQuality = (100 - 10 * gfp->VBR_q - gfp->quality);
+
+
+ /*
+ NOTE:
+ Even though the specification for the LAME VBR tag
+ did explicitly mention other encoders than LAME,
+ many SW/HW decoder seem to be able to make use of
+ this tag only, if the encoder version starts with LAME.
+ To be compatible with such decoders, ANY encoder will
+ be forced to write a fake LAME version string!
+ As a result, the encoder version info becomes worthless.
+ */
+ const char *szVersion = get_lame_tag_encoder_short_version();
+ uint8_t nVBR;
+ uint8_t nRevision = 0x00;
+ uint8_t nRevMethod;
+ uint8_t vbr_type_translator[] = { 1, 5, 3, 2, 4, 0, 3 }; /*numbering different in vbr_mode vs. Lame tag */
+
+ uint8_t nLowpass =
+ (((cfg->lowpassfreq / 100.0) + .5) > 255 ? 255 : (cfg->lowpassfreq / 100.0) + .5);
+
+ uint32_t nPeakSignalAmplitude = 0;
+
+ uint16_t nRadioReplayGain = 0;
+ uint16_t nAudiophileReplayGain = 0;
+
+ uint8_t nNoiseShaping = cfg->noise_shaping;
+ uint8_t nStereoMode = 0;
+ int bNonOptimal = 0;
+ uint8_t nSourceFreq = 0;
+ uint8_t nMisc = 0;
+ uint16_t nMusicCRC = 0;
+
+ /*psy model type: Gpsycho or NsPsytune */
+ unsigned char bExpNPsyTune = 1; /* only NsPsytune */
+ unsigned char bSafeJoint = (cfg->use_safe_joint_stereo) != 0;
+
+ unsigned char bNoGapMore = 0;
+ unsigned char bNoGapPrevious = 0;
+
+ int nNoGapCount = gfp->nogap_total;
+ int nNoGapCurr = gfp->nogap_current;
+
+
+ uint8_t nAthType = cfg->ATHtype; /*4 bits. */
+
+ uint8_t nFlags = 0;
+
+ /* if ABR, {store bitrate <=255} else { store "-b"} */
+ int nABRBitrate;
+ switch (cfg->vbr) {
+ case vbr_abr:{
+ nABRBitrate = cfg->vbr_avg_bitrate_kbps;
+ break;
+ }
+ case vbr_off:{
+ nABRBitrate = cfg->avg_bitrate;
+ break;
+ }
+ default:{ /*vbr modes */
+ nABRBitrate = bitrate_table[cfg->version][cfg->vbr_min_bitrate_index];;
+ }
+ }
+
+
+ /*revision and vbr method */
+ if (cfg->vbr < sizeof(vbr_type_translator))
+ nVBR = vbr_type_translator[cfg->vbr];
+ else
+ nVBR = 0x00; /*unknown. */
+
+ nRevMethod = 0x10 * nRevision + nVBR;
+
+
+ /* ReplayGain */
+ if (cfg->findReplayGain) {
+ int RadioGain = gfc->ov_rpg.RadioGain;
+ if (RadioGain > 0x1FE)
+ RadioGain = 0x1FE;
+ if (RadioGain < -0x1FE)
+ RadioGain = -0x1FE;
+
+ nRadioReplayGain = 0x2000; /* set name code */
+ nRadioReplayGain |= 0xC00; /* set originator code to `determined automatically' */
+
+ if (RadioGain >= 0)
+ nRadioReplayGain |= RadioGain; /* set gain adjustment */
+ else {
+ nRadioReplayGain |= 0x200; /* set the sign bit */
+ nRadioReplayGain |= -RadioGain; /* set gain adjustment */
+ }
+ }
+
+ /* peak sample */
+ if (cfg->findPeakSample)
+ nPeakSignalAmplitude =
+ abs((int) ((((FLOAT) gfc->ov_rpg.PeakSample) / 32767.0) * pow(2, 23) + .5));
+
+ /*nogap */
+ if (nNoGapCount != -1) {
+ if (nNoGapCurr > 0)
+ bNoGapPrevious = 1;
+
+ if (nNoGapCurr < nNoGapCount - 1)
+ bNoGapMore = 1;
+ }
+
+ /*flags */
+
+ nFlags = nAthType + (bExpNPsyTune << 4)
+ + (bSafeJoint << 5)
+ + (bNoGapMore << 6)
+ + (bNoGapPrevious << 7);
+
+
+ if (nQuality < 0)
+ nQuality = 0;
+
+ /*stereo mode field... a bit ugly. */
+
+ switch (cfg->mode) {
+ case MONO:
+ nStereoMode = 0;
+ break;
+ case STEREO:
+ nStereoMode = 1;
+ break;
+ case DUAL_CHANNEL:
+ nStereoMode = 2;
+ break;
+ case JOINT_STEREO:
+ if (cfg->force_ms)
+ nStereoMode = 4;
+ else
+ nStereoMode = 3;
+ break;
+ case NOT_SET:
+ /* FALLTHROUGH */
+ default:
+ nStereoMode = 7;
+ break;
+ }
+
+ /*Intensity stereo : nStereoMode = 6. IS is not implemented */
+
+ if (cfg->samplerate_in <= 32000)
+ nSourceFreq = 0x00;
+ else if (cfg->samplerate_in == 48000)
+ nSourceFreq = 0x02;
+ else if (cfg->samplerate_in > 48000)
+ nSourceFreq = 0x03;
+ else
+ nSourceFreq = 0x01; /*default is 44100Hz. */
+
+
+ /*Check if the user overrided the default LAME behaviour with some nasty options */
+
+ if (cfg->short_blocks == short_block_forced || cfg->short_blocks == short_block_dispensed || ((cfg->lowpassfreq == -1) && (cfg->highpassfreq == -1)) || /* "-k" */
+ (cfg->disable_reservoir && cfg->avg_bitrate < 320) ||
+ cfg->noATH || cfg->ATHonly || (nAthType == 0) || cfg->samplerate_in <= 32000)
+ bNonOptimal = 1;
+
+ nMisc = nNoiseShaping + (nStereoMode << 2)
+ + (bNonOptimal << 5)
+ + (nSourceFreq << 6);
+
+
+ nMusicCRC = gfc->nMusicCRC;
+
+
+ /*Write all this information into the stream */
+ CreateI4(&pbtStreamBuffer[nBytesWritten], nQuality);
+ nBytesWritten += 4;
+
+ strncpy((char *) &pbtStreamBuffer[nBytesWritten], szVersion, 9);
+ nBytesWritten += 9;
+
+ pbtStreamBuffer[nBytesWritten] = nRevMethod;
+ nBytesWritten++;
+
+ pbtStreamBuffer[nBytesWritten] = nLowpass;
+ nBytesWritten++;
+
+ CreateI4(&pbtStreamBuffer[nBytesWritten], nPeakSignalAmplitude);
+ nBytesWritten += 4;
+
+ CreateI2(&pbtStreamBuffer[nBytesWritten], nRadioReplayGain);
+ nBytesWritten += 2;
+
+ CreateI2(&pbtStreamBuffer[nBytesWritten], nAudiophileReplayGain);
+ nBytesWritten += 2;
+
+ pbtStreamBuffer[nBytesWritten] = nFlags;
+ nBytesWritten++;
+
+ if (nABRBitrate >= 255)
+ pbtStreamBuffer[nBytesWritten] = 0xFF;
+ else
+ pbtStreamBuffer[nBytesWritten] = nABRBitrate;
+ nBytesWritten++;
+
+ pbtStreamBuffer[nBytesWritten] = enc_delay >> 4; /* works for win32, does it for unix? */
+ pbtStreamBuffer[nBytesWritten + 1] = (enc_delay << 4) + (enc_padding >> 8);
+ pbtStreamBuffer[nBytesWritten + 2] = enc_padding;
+
+ nBytesWritten += 3;
+
+ pbtStreamBuffer[nBytesWritten] = nMisc;
+ nBytesWritten++;
+
+
+ pbtStreamBuffer[nBytesWritten++] = 0; /*unused in rev0 */
+
+ CreateI2(&pbtStreamBuffer[nBytesWritten], cfg->preset);
+ nBytesWritten += 2;
+
+ CreateI4(&pbtStreamBuffer[nBytesWritten], (int) nMusicLength);
+ nBytesWritten += 4;
+
+ CreateI2(&pbtStreamBuffer[nBytesWritten], nMusicCRC);
+ nBytesWritten += 2;
+
+ /*Calculate tag CRC.... must be done here, since it includes
+ *previous information*/
+
+ for (i = 0; i < nBytesWritten; i++)
+ crc = CRC_update_lookup(pbtStreamBuffer[i], crc);
+
+ CreateI2(&pbtStreamBuffer[nBytesWritten], crc);
+ nBytesWritten += 2;
+
+ return nBytesWritten;
+}
+
+static long
+skipId3v2(FILE * fpStream)
+{
+ size_t nbytes;
+ long id3v2TagSize;
+ unsigned char id3v2Header[10];
+
+ /* seek to the beginning of the stream */
+ if (fseek(fpStream, 0, SEEK_SET) != 0) {
+ return -2; /* not seekable, abort */
+ }
+ /* read 10 bytes in case there's an ID3 version 2 header here */
+ nbytes = fread(id3v2Header, 1, sizeof(id3v2Header), fpStream);
+ if (nbytes != sizeof(id3v2Header)) {
+ return -3; /* not readable, maybe opened Write-Only */
+ }
+ /* does the stream begin with the ID3 version 2 file identifier? */
+ if (!strncmp((char *) id3v2Header, "ID3", 3)) {
+ /* the tag size (minus the 10-byte header) is encoded into four
+ * bytes where the most significant bit is clear in each byte */
+ id3v2TagSize = (((id3v2Header[6] & 0x7f) << 21)
+ | ((id3v2Header[7] & 0x7f) << 14)
+ | ((id3v2Header[8] & 0x7f) << 7)
+ | (id3v2Header[9] & 0x7f))
+ + sizeof id3v2Header;
+ }
+ else {
+ /* no ID3 version 2 tag in this stream */
+ id3v2TagSize = 0;
+ }
+ return id3v2TagSize;
+}
+
+
+
+size_t
+lame_get_lametag_frame(lame_global_flags const *gfp, unsigned char *buffer, size_t size)
+{
+ lame_internal_flags *gfc;
+ SessionConfig_t const *cfg;
+ unsigned long stream_size;
+ unsigned int nStreamIndex;
+ uint8_t btToc[NUMTOCENTRIES];
+
+ if (gfp == 0) {
+ return 0;
+ }
+ gfc = gfp->internal_flags;
+ if (gfc == 0) {
+ return 0;
+ }
+ if (gfc->class_id != LAME_ID) {
+ return 0;
+ }
+ cfg = &gfc->cfg;
+ if (cfg->write_lame_tag == 0) {
+ return 0;
+ }
+ if (gfc->VBR_seek_table.pos <= 0) {
+ return 0;
+ }
+ if (size < gfc->VBR_seek_table.TotalFrameSize) {
+ return gfc->VBR_seek_table.TotalFrameSize;
+ }
+ if (buffer == 0) {
+ return 0;
+ }
+
+ memset(buffer, 0, gfc->VBR_seek_table.TotalFrameSize);
+
+ /* 4 bytes frame header */
+
+ setLameTagFrameHeader(gfc, buffer);
+
+ /* Clear all TOC entries */
+ memset(btToc, 0, sizeof(btToc));
+
+ if (cfg->free_format) {
+ int i;
+ for (i = 1; i < NUMTOCENTRIES; ++i)
+ btToc[i] = 255 * i / 100;
+ }
+ else {
+ Xing_seek_table(&gfc->VBR_seek_table, btToc);
+ }
+#ifdef DEBUG_VBR_SEEKING_TABLE
+ print_seeking(btToc);
+#endif
+
+ /* Start writing the tag after the zero frame */
+ nStreamIndex = cfg->sideinfo_len;
+ /* note! Xing header specifies that Xing data goes in the
+ * ancillary data with NO ERROR PROTECTION. If error protecton
+ * in enabled, the Xing data still starts at the same offset,
+ * and now it is in sideinfo data block, and thus will not
+ * decode correctly by non-Xing tag aware players */
+ if (cfg->error_protection)
+ nStreamIndex -= 2;
+
+ /* Put Vbr tag */
+ if (cfg->vbr == vbr_off) {
+ buffer[nStreamIndex++] = VBRTag1[0];
+ buffer[nStreamIndex++] = VBRTag1[1];
+ buffer[nStreamIndex++] = VBRTag1[2];
+ buffer[nStreamIndex++] = VBRTag1[3];
+
+ }
+ else {
+ buffer[nStreamIndex++] = VBRTag0[0];
+ buffer[nStreamIndex++] = VBRTag0[1];
+ buffer[nStreamIndex++] = VBRTag0[2];
+ buffer[nStreamIndex++] = VBRTag0[3];
+ }
+
+ /* Put header flags */
+ CreateI4(&buffer[nStreamIndex], FRAMES_FLAG + BYTES_FLAG + TOC_FLAG + VBR_SCALE_FLAG);
+ nStreamIndex += 4;
+
+ /* Put Total Number of frames */
+ CreateI4(&buffer[nStreamIndex], gfc->VBR_seek_table.nVbrNumFrames);
+ nStreamIndex += 4;
+
+ /* Put total audio stream size, including Xing/LAME Header */
+ stream_size = gfc->VBR_seek_table.nBytesWritten + gfc->VBR_seek_table.TotalFrameSize;
+ CreateI4(&buffer[nStreamIndex], stream_size);
+ nStreamIndex += 4;
+
+ /* Put TOC */
+ memcpy(&buffer[nStreamIndex], btToc, sizeof(btToc));
+ nStreamIndex += sizeof(btToc);
+
+
+ if (cfg->error_protection) {
+ /* (jo) error_protection: add crc16 information to header */
+ CRC_writeheader(gfc, (char *) buffer);
+ }
+ {
+ /*work out CRC so far: initially crc = 0 */
+ uint16_t crc = 0x00;
+ unsigned int i;
+ for (i = 0; i < nStreamIndex; i++)
+ crc = CRC_update_lookup(buffer[i], crc);
+ /*Put LAME VBR info */
+ nStreamIndex += PutLameVBR(gfp, stream_size, buffer + nStreamIndex, crc);
+ }
+
+#ifdef DEBUG_VBRTAG
+ {
+ VBRTAGDATA TestHeader;
+ GetVbrTag(&TestHeader, buffer);
+ }
+#endif
+
+ return gfc->VBR_seek_table.TotalFrameSize;
+}
+
+/***********************************************************************
+ *
+ * PutVbrTag: Write final VBR tag to the file
+ * Paramters:
+ * lpszFileName: filename of MP3 bit stream
+ * nVbrScale : encoder quality indicator (0..100)
+ ****************************************************************************
+ */
+
+int
+PutVbrTag(lame_global_flags const *gfp, FILE * fpStream)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+
+ long lFileSize;
+ long id3v2TagSize;
+ size_t nbytes;
+ uint8_t buffer[MAXFRAMESIZE];
+
+ if (gfc->VBR_seek_table.pos <= 0)
+ return -1;
+
+ /* Seek to end of file */
+ fseek(fpStream, 0, SEEK_END);
+
+ /* Get file size */
+ lFileSize = ftell(fpStream);
+
+ /* Abort if file has zero length. Yes, it can happen :) */
+ if (lFileSize == 0)
+ return -1;
+
+ /*
+ * The VBR tag may NOT be located at the beginning of the stream.
+ * If an ID3 version 2 tag was added, then it must be skipped to write
+ * the VBR tag data.
+ */
+
+ id3v2TagSize = skipId3v2(fpStream);
+
+ if (id3v2TagSize < 0) {
+ return id3v2TagSize;
+ }
+
+ /*Seek to the beginning of the stream */
+ fseek(fpStream, id3v2TagSize, SEEK_SET);
+
+ nbytes = lame_get_lametag_frame(gfp, buffer, sizeof(buffer));
+ if (nbytes > sizeof(buffer)) {
+ return -1;
+ }
+
+ if (nbytes < 1) {
+ return 0;
+ }
+
+ /* Put it all to disk again */
+ if (fwrite(buffer, nbytes, 1, fpStream) != 1) {
+ return -1;
+ }
+
+ return 0; /* success */
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/VbrTag.h b/libnative/src/main/cpp/module/mp3/lame/VbrTag.h
new file mode 100644
index 0000000000000000000000000000000000000000..406af3667d2e02ac71a2009e01d43b5bb269d1cc
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/VbrTag.h
@@ -0,0 +1,79 @@
+/*
+ * Xing VBR tagging for LAME.
+ *
+ * Copyright (c) 1999 A.L. Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_VRBTAG_H
+#define LAME_VRBTAG_H
+
+
+/* -----------------------------------------------------------
+ * A Vbr header may be present in the ancillary
+ * data field of the first frame of an mp3 bitstream
+ * The Vbr header (optionally) contains
+ * frames total number of audio frames in the bitstream
+ * bytes total number of bytes in the bitstream
+ * toc table of contents
+
+ * toc (table of contents) gives seek points
+ * for random access
+ * the ith entry determines the seek point for
+ * i-percent duration
+ * seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
+ * e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
+ */
+
+
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define NUMTOCENTRIES 100
+
+#ifndef lame_internal_flags_defined
+#define lame_internal_flags_defined
+struct lame_internal_flags;
+typedef struct lame_internal_flags lame_internal_flags;
+#endif
+
+
+/*structure to receive extracted header */
+/* toc may be NULL*/
+typedef struct {
+ int h_id; /* from MPEG header, 0=MPEG2, 1=MPEG1 */
+ int samprate; /* determined from MPEG header */
+ int flags; /* from Vbr header data */
+ int frames; /* total bit stream frames from Vbr header data */
+ int bytes; /* total bit stream bytes from Vbr header data */
+ int vbr_scale; /* encoded vbr scale from Vbr header data */
+ unsigned char toc[NUMTOCENTRIES]; /* may be NULL if toc not desired */
+ int headersize; /* size of VBR header, in bytes */
+ int enc_delay; /* encoder delay */
+ int enc_padding; /* encoder paddign added at end of stream */
+} VBRTAGDATA;
+
+int GetVbrTag(VBRTAGDATA * pTagData, const unsigned char *buf);
+
+int InitVbrTag(lame_global_flags * gfp);
+int PutVbrTag(lame_global_flags const *gfp, FILE * fid);
+void AddVbrFrame(lame_internal_flags * gfc);
+void UpdateMusicCRC(uint16_t * crc, const unsigned char *buffer, int size);
+
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/bitstream.c b/libnative/src/main/cpp/module/mp3/lame/bitstream.c
new file mode 100644
index 0000000000000000000000000000000000000000..b11edfb2b05f0b8f3989537e5b25790390e0950d
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/bitstream.c
@@ -0,0 +1,1111 @@
+/*
+ * MP3 bitstream Output interface for LAME
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 1999-2002 Takehiro Tominaga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id: bitstream.c,v 1.97 2011/05/07 16:05:17 rbrito Exp $
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "tables.h"
+#include "quantize_pvt.h"
+#include "lame_global_flags.h"
+#include "gain_analysis.h"
+#include "VbrTag.h"
+#include "bitstream.h"
+#include "tables.h"
+
+
+
+/* unsigned int is at least this large: */
+/* we work with ints, so when doing bit manipulation, we limit
+ * ourselves to MAX_LENGTH-2 just to be on the safe side */
+#define MAX_LENGTH 32
+
+
+
+#ifdef DEBUG
+static int hogege;
+#endif
+
+
+
+static int
+calcFrameLength(SessionConfig_t const *const cfg, int kbps, int pad)
+{
+ return 8 * ((cfg->version + 1) * 72000 * kbps / cfg->samplerate_out + pad);
+}
+
+
+/***********************************************************************
+ * compute bitsperframe and mean_bits for a layer III frame
+ **********************************************************************/
+int
+getframebits(const lame_internal_flags * gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int bit_rate;
+
+ /* get bitrate in kbps [?] */
+ if (eov->bitrate_index)
+ bit_rate = bitrate_table[cfg->version][eov->bitrate_index];
+ else
+ bit_rate = cfg->avg_bitrate;
+ /*assert(bit_rate <= 550); */
+ assert(8 <= bit_rate && bit_rate <= 640);
+
+ /* main encoding routine toggles padding on and off */
+ /* one Layer3 Slot consists of 8 bits */
+ return calcFrameLength(cfg, bit_rate, eov->padding);
+}
+
+int
+get_max_frame_buffer_size_by_constraint(SessionConfig_t const * cfg, int constraint)
+{
+ int maxmp3buf = 0;
+ if (cfg->avg_bitrate > 320) {
+ /* in freeformat the buffer is constant */
+ if (constraint == MDB_STRICT_ISO) {
+ maxmp3buf = calcFrameLength(cfg, cfg->avg_bitrate, 0);
+ }
+ else {
+ /* maximum allowed bits per granule are 7680 */
+ maxmp3buf = 7680 * (cfg->version + 1);
+ }
+ }
+ else {
+ int max_kbps;
+ if (cfg->samplerate_out < 16000) {
+ max_kbps = bitrate_table[cfg->version][8]; /* default: allow 64 kbps (MPEG-2.5) */
+ }
+ else {
+ max_kbps = bitrate_table[cfg->version][14];
+ }
+ switch (constraint)
+ {
+ default:
+ case MDB_DEFAULT:
+ /* Bouvigne suggests this more lax interpretation of the ISO doc instead of using 8*960. */
+ /* All mp3 decoders should have enough buffer to handle this value: size of a 320kbps 32kHz frame */
+ maxmp3buf = 8 * 1440;
+ break;
+ case MDB_STRICT_ISO:
+ maxmp3buf = calcFrameLength(cfg, max_kbps, 0);
+ break;
+ case MDB_MAXIMUM:
+ maxmp3buf = 7680 * (cfg->version + 1);
+ break;
+ }
+ }
+ return maxmp3buf;
+}
+
+
+static void
+putheader_bits(lame_internal_flags * gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ Bit_stream_struc *bs = &gfc->bs;
+#ifdef DEBUG
+ hogege += cfg->sideinfo_len * 8;
+#endif
+ memcpy(&bs->buf[bs->buf_byte_idx], esv->header[esv->w_ptr].buf, cfg->sideinfo_len);
+ bs->buf_byte_idx += cfg->sideinfo_len;
+ bs->totbit += cfg->sideinfo_len * 8;
+ esv->w_ptr = (esv->w_ptr + 1) & (MAX_HEADER_BUF - 1);
+}
+
+
+
+
+/*write j bits into the bit stream */
+inline static void
+putbits2(lame_internal_flags * gfc, int val, int j)
+{
+ EncStateVar_t const *const esv = &gfc->sv_enc;
+ Bit_stream_struc *bs;
+ bs = &gfc->bs;
+
+ assert(j < MAX_LENGTH - 2);
+
+ while (j > 0) {
+ int k;
+ if (bs->buf_bit_idx == 0) {
+ bs->buf_bit_idx = 8;
+ bs->buf_byte_idx++;
+ assert(bs->buf_byte_idx < BUFFER_SIZE);
+ assert(esv->header[esv->w_ptr].write_timing >= bs->totbit);
+ if (esv->header[esv->w_ptr].write_timing == bs->totbit) {
+ putheader_bits(gfc);
+ }
+ bs->buf[bs->buf_byte_idx] = 0;
+ }
+
+ k = Min(j, bs->buf_bit_idx);
+ j -= k;
+
+ bs->buf_bit_idx -= k;
+
+ assert(j < MAX_LENGTH); /* 32 too large on 32 bit machines */
+ assert(bs->buf_bit_idx < MAX_LENGTH);
+
+ bs->buf[bs->buf_byte_idx] |= ((val >> j) << bs->buf_bit_idx);
+ bs->totbit += k;
+ }
+}
+
+/*write j bits into the bit stream, ignoring frame headers */
+inline static void
+putbits_noheaders(lame_internal_flags * gfc, int val, int j)
+{
+ Bit_stream_struc *bs;
+ bs = &gfc->bs;
+
+ assert(j < MAX_LENGTH - 2);
+
+ while (j > 0) {
+ int k;
+ if (bs->buf_bit_idx == 0) {
+ bs->buf_bit_idx = 8;
+ bs->buf_byte_idx++;
+ assert(bs->buf_byte_idx < BUFFER_SIZE);
+ bs->buf[bs->buf_byte_idx] = 0;
+ }
+
+ k = Min(j, bs->buf_bit_idx);
+ j -= k;
+
+ bs->buf_bit_idx -= k;
+
+ assert(j < MAX_LENGTH); /* 32 too large on 32 bit machines */
+ assert(bs->buf_bit_idx < MAX_LENGTH);
+
+ bs->buf[bs->buf_byte_idx] |= ((val >> j) << bs->buf_bit_idx);
+ bs->totbit += k;
+ }
+}
+
+
+/*
+ Some combinations of bitrate, Fs, and stereo make it impossible to stuff
+ out a frame using just main_data, due to the limited number of bits to
+ indicate main_data_length. In these situations, we put stuffing bits into
+ the ancillary data...
+*/
+
+inline static void
+drain_into_ancillary(lame_internal_flags * gfc, int remainingBits)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int i;
+ assert(remainingBits >= 0);
+
+ if (remainingBits >= 8) {
+ putbits2(gfc, 0x4c, 8);
+ remainingBits -= 8;
+ }
+ if (remainingBits >= 8) {
+ putbits2(gfc, 0x41, 8);
+ remainingBits -= 8;
+ }
+ if (remainingBits >= 8) {
+ putbits2(gfc, 0x4d, 8);
+ remainingBits -= 8;
+ }
+ if (remainingBits >= 8) {
+ putbits2(gfc, 0x45, 8);
+ remainingBits -= 8;
+ }
+
+ if (remainingBits >= 32) {
+ const char *const version = get_lame_short_version();
+ if (remainingBits >= 32)
+ for (i = 0; i < (int) strlen(version) && remainingBits >= 8; ++i) {
+ remainingBits -= 8;
+ putbits2(gfc, version[i], 8);
+ }
+ }
+
+ for (; remainingBits >= 1; remainingBits -= 1) {
+ putbits2(gfc, esv->ancillary_flag, 1);
+ esv->ancillary_flag ^= !cfg->disable_reservoir;
+ }
+
+ assert(remainingBits == 0);
+
+}
+
+/*write N bits into the header */
+inline static void
+writeheader(lame_internal_flags * gfc, int val, int j)
+{
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int ptr = esv->header[esv->h_ptr].ptr;
+
+ while (j > 0) {
+ int const k = Min(j, 8 - (ptr & 7));
+ j -= k;
+ assert(j < MAX_LENGTH); /* >> 32 too large for 32 bit machines */
+ esv->header[esv->h_ptr].buf[ptr >> 3]
+ |= ((val >> j)) << (8 - (ptr & 7) - k);
+ ptr += k;
+ }
+ esv->header[esv->h_ptr].ptr = ptr;
+}
+
+
+static int
+CRC_update(int value, int crc)
+{
+ int i;
+ value <<= 8;
+ for (i = 0; i < 8; i++) {
+ value <<= 1;
+ crc <<= 1;
+
+ if (((crc ^ value) & 0x10000))
+ crc ^= CRC16_POLYNOMIAL;
+ }
+ return crc;
+}
+
+
+void
+CRC_writeheader(lame_internal_flags const *gfc, char *header)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int crc = 0xffff; /* (jo) init crc16 for error_protection */
+ int i;
+
+ crc = CRC_update(((unsigned char *) header)[2], crc);
+ crc = CRC_update(((unsigned char *) header)[3], crc);
+ for (i = 6; i < cfg->sideinfo_len; i++) {
+ crc = CRC_update(((unsigned char *) header)[i], crc);
+ }
+
+ header[4] = crc >> 8;
+ header[5] = crc & 255;
+}
+
+inline static void
+encodeSideInfo2(lame_internal_flags * gfc, int bitsPerFrame)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ III_side_info_t *l3_side;
+ int gr, ch;
+
+ l3_side = &gfc->l3_side;
+ esv->header[esv->h_ptr].ptr = 0;
+ memset(esv->header[esv->h_ptr].buf, 0, cfg->sideinfo_len);
+ if (cfg->samplerate_out < 16000)
+ writeheader(gfc, 0xffe, 12);
+ else
+ writeheader(gfc, 0xfff, 12);
+ writeheader(gfc, (cfg->version), 1);
+ writeheader(gfc, 4 - 3, 2);
+ writeheader(gfc, (!cfg->error_protection), 1);
+ writeheader(gfc, (eov->bitrate_index), 4);
+ writeheader(gfc, (cfg->samplerate_index), 2);
+ writeheader(gfc, (eov->padding), 1);
+ writeheader(gfc, (cfg->extension), 1);
+ writeheader(gfc, (cfg->mode), 2);
+ writeheader(gfc, (eov->mode_ext), 2);
+ writeheader(gfc, (cfg->copyright), 1);
+ writeheader(gfc, (cfg->original), 1);
+ writeheader(gfc, (cfg->emphasis), 2);
+ if (cfg->error_protection) {
+ writeheader(gfc, 0, 16); /* dummy */
+ }
+
+ if (cfg->version == 1) {
+ /* MPEG1 */
+ assert(l3_side->main_data_begin >= 0);
+ writeheader(gfc, (l3_side->main_data_begin), 9);
+
+ if (cfg->channels_out == 2)
+ writeheader(gfc, l3_side->private_bits, 3);
+ else
+ writeheader(gfc, l3_side->private_bits, 5);
+
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ int band;
+ for (band = 0; band < 4; band++) {
+ writeheader(gfc, l3_side->scfsi[ch][band], 1);
+ }
+ }
+
+ for (gr = 0; gr < 2; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info *const gi = &l3_side->tt[gr][ch];
+ writeheader(gfc, gi->part2_3_length + gi->part2_length, 12);
+ writeheader(gfc, gi->big_values / 2, 9);
+ writeheader(gfc, gi->global_gain, 8);
+ writeheader(gfc, gi->scalefac_compress, 4);
+
+ if (gi->block_type != NORM_TYPE) {
+ writeheader(gfc, 1, 1); /* window_switching_flag */
+ writeheader(gfc, gi->block_type, 2);
+ writeheader(gfc, gi->mixed_block_flag, 1);
+
+ if (gi->table_select[0] == 14)
+ gi->table_select[0] = 16;
+ writeheader(gfc, gi->table_select[0], 5);
+ if (gi->table_select[1] == 14)
+ gi->table_select[1] = 16;
+ writeheader(gfc, gi->table_select[1], 5);
+
+ writeheader(gfc, gi->subblock_gain[0], 3);
+ writeheader(gfc, gi->subblock_gain[1], 3);
+ writeheader(gfc, gi->subblock_gain[2], 3);
+ }
+ else {
+ writeheader(gfc, 0, 1); /* window_switching_flag */
+ if (gi->table_select[0] == 14)
+ gi->table_select[0] = 16;
+ writeheader(gfc, gi->table_select[0], 5);
+ if (gi->table_select[1] == 14)
+ gi->table_select[1] = 16;
+ writeheader(gfc, gi->table_select[1], 5);
+ if (gi->table_select[2] == 14)
+ gi->table_select[2] = 16;
+ writeheader(gfc, gi->table_select[2], 5);
+
+ assert(0 <= gi->region0_count && gi->region0_count < 16);
+ assert(0 <= gi->region1_count && gi->region1_count < 8);
+ writeheader(gfc, gi->region0_count, 4);
+ writeheader(gfc, gi->region1_count, 3);
+ }
+ writeheader(gfc, gi->preflag, 1);
+ writeheader(gfc, gi->scalefac_scale, 1);
+ writeheader(gfc, gi->count1table_select, 1);
+ }
+ }
+ }
+ else {
+ /* MPEG2 */
+ assert(l3_side->main_data_begin >= 0);
+ writeheader(gfc, (l3_side->main_data_begin), 8);
+ writeheader(gfc, l3_side->private_bits, cfg->channels_out);
+
+ gr = 0;
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info *const gi = &l3_side->tt[gr][ch];
+ writeheader(gfc, gi->part2_3_length + gi->part2_length, 12);
+ writeheader(gfc, gi->big_values / 2, 9);
+ writeheader(gfc, gi->global_gain, 8);
+ writeheader(gfc, gi->scalefac_compress, 9);
+
+ if (gi->block_type != NORM_TYPE) {
+ writeheader(gfc, 1, 1); /* window_switching_flag */
+ writeheader(gfc, gi->block_type, 2);
+ writeheader(gfc, gi->mixed_block_flag, 1);
+
+ if (gi->table_select[0] == 14)
+ gi->table_select[0] = 16;
+ writeheader(gfc, gi->table_select[0], 5);
+ if (gi->table_select[1] == 14)
+ gi->table_select[1] = 16;
+ writeheader(gfc, gi->table_select[1], 5);
+
+ writeheader(gfc, gi->subblock_gain[0], 3);
+ writeheader(gfc, gi->subblock_gain[1], 3);
+ writeheader(gfc, gi->subblock_gain[2], 3);
+ }
+ else {
+ writeheader(gfc, 0, 1); /* window_switching_flag */
+ if (gi->table_select[0] == 14)
+ gi->table_select[0] = 16;
+ writeheader(gfc, gi->table_select[0], 5);
+ if (gi->table_select[1] == 14)
+ gi->table_select[1] = 16;
+ writeheader(gfc, gi->table_select[1], 5);
+ if (gi->table_select[2] == 14)
+ gi->table_select[2] = 16;
+ writeheader(gfc, gi->table_select[2], 5);
+
+ assert(0 <= gi->region0_count && gi->region0_count < 16);
+ assert(0 <= gi->region1_count && gi->region1_count < 8);
+ writeheader(gfc, gi->region0_count, 4);
+ writeheader(gfc, gi->region1_count, 3);
+ }
+
+ writeheader(gfc, gi->scalefac_scale, 1);
+ writeheader(gfc, gi->count1table_select, 1);
+ }
+ }
+
+ if (cfg->error_protection) {
+ /* (jo) error_protection: add crc16 information to header */
+ CRC_writeheader(gfc, esv->header[esv->h_ptr].buf);
+ }
+
+ {
+ int const old = esv->h_ptr;
+ assert(esv->header[old].ptr == cfg->sideinfo_len * 8);
+
+ esv->h_ptr = (old + 1) & (MAX_HEADER_BUF - 1);
+ esv->header[esv->h_ptr].write_timing = esv->header[old].write_timing + bitsPerFrame;
+
+ if (esv->h_ptr == esv->w_ptr) {
+ /* yikes! we are out of header buffer space */
+ ERRORF(gfc, "Error: MAX_HEADER_BUF too small in bitstream.c \n");
+ }
+
+ }
+}
+
+
+inline static int
+huffman_coder_count1(lame_internal_flags * gfc, gr_info const *gi)
+{
+ /* Write count1 area */
+ struct huffcodetab const *const h = &ht[gi->count1table_select + 32];
+ int i, bits = 0;
+#ifdef DEBUG
+ int gegebo = gfc->bs.totbit;
+#endif
+
+ int const *ix = &gi->l3_enc[gi->big_values];
+ FLOAT const *xr = &gi->xr[gi->big_values];
+ assert(gi->count1table_select < 2);
+
+ for (i = (gi->count1 - gi->big_values) / 4; i > 0; --i) {
+ int huffbits = 0;
+ int p = 0, v;
+
+ v = ix[0];
+ if (v) {
+ p += 8;
+ if (xr[0] < 0.0f)
+ huffbits++;
+ assert(v <= 1);
+ }
+
+ v = ix[1];
+ if (v) {
+ p += 4;
+ huffbits *= 2;
+ if (xr[1] < 0.0f)
+ huffbits++;
+ assert(v <= 1);
+ }
+
+ v = ix[2];
+ if (v) {
+ p += 2;
+ huffbits *= 2;
+ if (xr[2] < 0.0f)
+ huffbits++;
+ assert(v <= 1);
+ }
+
+ v = ix[3];
+ if (v) {
+ p++;
+ huffbits *= 2;
+ if (xr[3] < 0.0f)
+ huffbits++;
+ assert(v <= 1);
+ }
+
+ ix += 4;
+ xr += 4;
+ putbits2(gfc, huffbits + h->table[p], h->hlen[p]);
+ bits += h->hlen[p];
+ }
+#ifdef DEBUG
+ DEBUGF(gfc, "count1: real: %ld counted:%d (bigv %d count1len %d)\n",
+ gfc->bs.totbit - gegebo, gi->count1bits, gi->big_values, gi->count1);
+#endif
+ return bits;
+}
+
+
+
+/*
+ Implements the pseudocode of page 98 of the IS
+ */
+inline static int
+Huffmancode(lame_internal_flags * const gfc, const unsigned int tableindex,
+ int start, int end, gr_info const *gi)
+{
+ struct huffcodetab const *const h = &ht[tableindex];
+ unsigned int const linbits = h->xlen;
+ int i, bits = 0;
+
+ assert(tableindex < 32u);
+ if (!tableindex)
+ return bits;
+
+ for (i = start; i < end; i += 2) {
+ int16_t cbits = 0;
+ uint16_t xbits = 0;
+ unsigned int xlen = h->xlen;
+ unsigned int ext = 0;
+ unsigned int x1 = gi->l3_enc[i];
+ unsigned int x2 = gi->l3_enc[i + 1];
+
+ assert(gi->l3_enc[i] >= 0);
+ assert(gi->l3_enc[i+1] >= 0);
+
+ if (x1 != 0u) {
+ if (gi->xr[i] < 0.0f)
+ ext++;
+ cbits--;
+ }
+
+ if (tableindex > 15u) {
+ /* use ESC-words */
+ if (x1 >= 15u) {
+ uint16_t const linbits_x1 = x1 - 15u;
+ assert(linbits_x1 <= h->linmax);
+ ext |= linbits_x1 << 1u;
+ xbits = linbits;
+ x1 = 15u;
+ }
+
+ if (x2 >= 15u) {
+ uint16_t const linbits_x2 = x2 - 15u;
+ assert(linbits_x2 <= h->linmax);
+ ext <<= linbits;
+ ext |= linbits_x2;
+ xbits += linbits;
+ x2 = 15u;
+ }
+ xlen = 16;
+ }
+
+ if (x2 != 0u) {
+ ext <<= 1;
+ if (gi->xr[i + 1] < 0.0f)
+ ext++;
+ cbits--;
+ }
+
+ assert((x1 | x2) < 16u);
+
+ x1 = x1 * xlen + x2;
+ xbits -= cbits;
+ cbits += h->hlen[x1];
+
+ assert(cbits <= MAX_LENGTH);
+ assert(xbits <= MAX_LENGTH);
+
+ putbits2(gfc, h->table[x1], cbits);
+ putbits2(gfc, (int)ext, xbits);
+ bits += cbits + xbits;
+ }
+ return bits;
+}
+
+/*
+ Note the discussion of huffmancodebits() on pages 28
+ and 29 of the IS, as well as the definitions of the side
+ information on pages 26 and 27.
+ */
+static int
+ShortHuffmancodebits(lame_internal_flags * gfc, gr_info const *gi)
+{
+ int bits;
+ int region1Start;
+
+ region1Start = 3 * gfc->scalefac_band.s[3];
+ if (region1Start > gi->big_values)
+ region1Start = gi->big_values;
+
+ /* short blocks do not have a region2 */
+ bits = Huffmancode(gfc, gi->table_select[0], 0, region1Start, gi);
+ bits += Huffmancode(gfc, gi->table_select[1], region1Start, gi->big_values, gi);
+ return bits;
+}
+
+static int
+LongHuffmancodebits(lame_internal_flags * gfc, gr_info const *gi)
+{
+ unsigned int i;
+ int bigvalues, bits;
+ int region1Start, region2Start;
+
+ bigvalues = gi->big_values;
+ assert(0 <= bigvalues && bigvalues <= 576);
+
+ assert(gi->region0_count >= -1);
+ assert(gi->region1_count >= -1);
+ i = gi->region0_count + 1;
+ assert((size_t) i < dimension_of(gfc->scalefac_band.l));
+ region1Start = gfc->scalefac_band.l[i];
+ i += gi->region1_count + 1;
+ assert((size_t) i < dimension_of(gfc->scalefac_band.l));
+ region2Start = gfc->scalefac_band.l[i];
+
+ if (region1Start > bigvalues)
+ region1Start = bigvalues;
+
+ if (region2Start > bigvalues)
+ region2Start = bigvalues;
+
+ bits = Huffmancode(gfc, gi->table_select[0], 0, region1Start, gi);
+ bits += Huffmancode(gfc, gi->table_select[1], region1Start, region2Start, gi);
+ bits += Huffmancode(gfc, gi->table_select[2], region2Start, bigvalues, gi);
+ return bits;
+}
+
+inline static int
+writeMainData(lame_internal_flags * const gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ III_side_info_t const *const l3_side = &gfc->l3_side;
+ int gr, ch, sfb, data_bits, tot_bits = 0;
+
+ if (cfg->version == 1) {
+ /* MPEG 1 */
+ for (gr = 0; gr < 2; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info const *const gi = &l3_side->tt[gr][ch];
+ int const slen1 = slen1_tab[gi->scalefac_compress];
+ int const slen2 = slen2_tab[gi->scalefac_compress];
+ data_bits = 0;
+#ifdef DEBUG
+ hogege = gfc->bs.totbit;
+#endif
+ for (sfb = 0; sfb < gi->sfbdivide; sfb++) {
+ if (gi->scalefac[sfb] == -1)
+ continue; /* scfsi is used */
+ putbits2(gfc, gi->scalefac[sfb], slen1);
+ data_bits += slen1;
+ }
+ for (; sfb < gi->sfbmax; sfb++) {
+ if (gi->scalefac[sfb] == -1)
+ continue; /* scfsi is used */
+ putbits2(gfc, gi->scalefac[sfb], slen2);
+ data_bits += slen2;
+ }
+ assert(data_bits == gi->part2_length);
+
+ if (gi->block_type == SHORT_TYPE) {
+ data_bits += ShortHuffmancodebits(gfc, gi);
+ }
+ else {
+ data_bits += LongHuffmancodebits(gfc, gi);
+ }
+ data_bits += huffman_coder_count1(gfc, gi);
+#ifdef DEBUG
+ DEBUGF(gfc, "<%ld> ", gfc->bs.totbit - hogege);
+#endif
+ /* does bitcount in quantize.c agree with actual bit count? */
+ assert(data_bits == gi->part2_3_length + gi->part2_length);
+ tot_bits += data_bits;
+ } /* for ch */
+ } /* for gr */
+ }
+ else {
+ /* MPEG 2 */
+ gr = 0;
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info const *const gi = &l3_side->tt[gr][ch];
+ int i, sfb_partition, scale_bits = 0;
+ assert(gi->sfb_partition_table);
+ data_bits = 0;
+#ifdef DEBUG
+ hogege = gfc->bs.totbit;
+#endif
+ sfb = 0;
+ sfb_partition = 0;
+
+ if (gi->block_type == SHORT_TYPE) {
+ for (; sfb_partition < 4; sfb_partition++) {
+ int const sfbs = gi->sfb_partition_table[sfb_partition] / 3;
+ int const slen = gi->slen[sfb_partition];
+ for (i = 0; i < sfbs; i++, sfb++) {
+ putbits2(gfc, Max(gi->scalefac[sfb * 3 + 0], 0), slen);
+ putbits2(gfc, Max(gi->scalefac[sfb * 3 + 1], 0), slen);
+ putbits2(gfc, Max(gi->scalefac[sfb * 3 + 2], 0), slen);
+ scale_bits += 3 * slen;
+ }
+ }
+ data_bits += ShortHuffmancodebits(gfc, gi);
+ }
+ else {
+ for (; sfb_partition < 4; sfb_partition++) {
+ int const sfbs = gi->sfb_partition_table[sfb_partition];
+ int const slen = gi->slen[sfb_partition];
+ for (i = 0; i < sfbs; i++, sfb++) {
+ putbits2(gfc, Max(gi->scalefac[sfb], 0), slen);
+ scale_bits += slen;
+ }
+ }
+ data_bits += LongHuffmancodebits(gfc, gi);
+ }
+ data_bits += huffman_coder_count1(gfc, gi);
+#ifdef DEBUG
+ DEBUGF(gfc, "<%ld> ", gfc->bs.totbit - hogege);
+#endif
+ /* does bitcount in quantize.c agree with actual bit count? */
+ assert(data_bits == gi->part2_3_length);
+ assert(scale_bits == gi->part2_length);
+ tot_bits += scale_bits + data_bits;
+ } /* for ch */
+ } /* for gf */
+ return tot_bits;
+} /* main_data */
+
+
+
+/* compute the number of bits required to flush all mp3 frames
+ currently in the buffer. This should be the same as the
+ reservoir size. Only call this routine between frames - i.e.
+ only after all headers and data have been added to the buffer
+ by format_bitstream().
+
+ Also compute total_bits_output =
+ size of mp3 buffer (including frame headers which may not
+ have yet been send to the mp3 buffer) +
+ number of bits needed to flush all mp3 frames.
+
+ total_bytes_output is the size of the mp3 output buffer if
+ lame_encode_flush_nogap() was called right now.
+
+ */
+int
+compute_flushbits(const lame_internal_flags * gfc, int *total_bytes_output)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t const *const esv = &gfc->sv_enc;
+ int flushbits, remaining_headers;
+ int bitsPerFrame;
+ int last_ptr, first_ptr;
+ first_ptr = esv->w_ptr; /* first header to add to bitstream */
+ last_ptr = esv->h_ptr - 1; /* last header to add to bitstream */
+ if (last_ptr == -1)
+ last_ptr = MAX_HEADER_BUF - 1;
+
+ /* add this many bits to bitstream so we can flush all headers */
+ flushbits = esv->header[last_ptr].write_timing - gfc->bs.totbit;
+ *total_bytes_output = flushbits;
+
+ if (flushbits >= 0) {
+ /* if flushbits >= 0, some headers have not yet been written */
+ /* reduce flushbits by the size of the headers */
+ remaining_headers = 1 + last_ptr - first_ptr;
+ if (last_ptr < first_ptr)
+ remaining_headers = 1 + last_ptr - first_ptr + MAX_HEADER_BUF;
+ flushbits -= remaining_headers * 8 * cfg->sideinfo_len;
+ }
+
+
+ /* finally, add some bits so that the last frame is complete
+ * these bits are not necessary to decode the last frame, but
+ * some decoders will ignore last frame if these bits are missing
+ */
+ bitsPerFrame = getframebits(gfc);
+ flushbits += bitsPerFrame;
+ *total_bytes_output += bitsPerFrame;
+ /* round up: */
+ if (*total_bytes_output % 8)
+ *total_bytes_output = 1 + (*total_bytes_output / 8);
+ else
+ *total_bytes_output = (*total_bytes_output / 8);
+ *total_bytes_output += gfc->bs.buf_byte_idx + 1;
+
+
+ if (flushbits < 0) {
+#if 0
+ /* if flushbits < 0, this would mean that the buffer looks like:
+ * (data...) last_header (data...) (extra data that should not be here...)
+ */
+ DEBUGF(gfc, "last header write_timing = %i \n", esv->header[last_ptr].write_timing);
+ DEBUGF(gfc, "first header write_timing = %i \n", esv->header[first_ptr].write_timing);
+ DEBUGF(gfc, "bs.totbit: %i \n", gfc->bs.totbit);
+ DEBUGF(gfc, "first_ptr, last_ptr %i %i \n", first_ptr, last_ptr);
+ DEBUGF(gfc, "remaining_headers = %i \n", remaining_headers);
+ DEBUGF(gfc, "bitsperframe: %i \n", bitsPerFrame);
+ DEBUGF(gfc, "sidelen: %i \n", cfg->sideinfo_len);
+#endif
+ ERRORF(gfc, "strange error flushing buffer ... \n");
+ }
+ return flushbits;
+}
+
+
+void
+flush_bitstream(lame_internal_flags * gfc)
+{
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ III_side_info_t *l3_side;
+ int nbytes;
+ int flushbits;
+ int last_ptr = esv->h_ptr - 1; /* last header to add to bitstream */
+ if (last_ptr == -1)
+ last_ptr = MAX_HEADER_BUF - 1;
+ l3_side = &gfc->l3_side;
+
+
+ if ((flushbits = compute_flushbits(gfc, &nbytes)) < 0)
+ return;
+ drain_into_ancillary(gfc, flushbits);
+
+ /* check that the 100% of the last frame has been written to bitstream */
+ assert(esv->header[last_ptr].write_timing + getframebits(gfc)
+ == gfc->bs.totbit);
+
+ /* we have padded out all frames with ancillary data, which is the
+ same as filling the bitreservoir with ancillary data, so : */
+ esv->ResvSize = 0;
+ l3_side->main_data_begin = 0;
+}
+
+
+
+
+void
+add_dummy_byte(lame_internal_flags * gfc, unsigned char val, unsigned int n)
+{
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int i;
+
+ while (n-- > 0u) {
+ putbits_noheaders(gfc, val, 8);
+
+ for (i = 0; i < MAX_HEADER_BUF; ++i)
+ esv->header[i].write_timing += 8;
+ }
+}
+
+
+/*
+ format_bitstream()
+
+ This is called after a frame of audio has been quantized and coded.
+ It will write the encoded audio to the bitstream. Note that
+ from a layer3 encoder's perspective the bit stream is primarily
+ a series of main_data() blocks, with header and side information
+ inserted at the proper locations to maintain framing. (See Figure A.7
+ in the IS).
+ */
+int
+format_bitstream(lame_internal_flags * gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int bits, nbytes;
+ III_side_info_t *l3_side;
+ int bitsPerFrame;
+ l3_side = &gfc->l3_side;
+
+ bitsPerFrame = getframebits(gfc);
+ drain_into_ancillary(gfc, l3_side->resvDrain_pre);
+
+ encodeSideInfo2(gfc, bitsPerFrame);
+ bits = 8 * cfg->sideinfo_len;
+ bits += writeMainData(gfc);
+ drain_into_ancillary(gfc, l3_side->resvDrain_post);
+ bits += l3_side->resvDrain_post;
+
+ l3_side->main_data_begin += (bitsPerFrame - bits) / 8;
+
+ /* compare number of bits needed to clear all buffered mp3 frames
+ * with what we think the resvsize is: */
+ if (compute_flushbits(gfc, &nbytes) != esv->ResvSize) {
+ ERRORF(gfc, "Internal buffer inconsistency. flushbits <> ResvSize");
+ }
+
+
+ /* compare main_data_begin for the next frame with what we
+ * think the resvsize is: */
+ if ((l3_side->main_data_begin * 8) != esv->ResvSize) {
+ ERRORF(gfc, "bit reservoir error: \n"
+ "l3_side->main_data_begin: %i \n"
+ "Resvoir size: %i \n"
+ "resv drain (post) %i \n"
+ "resv drain (pre) %i \n"
+ "header and sideinfo: %i \n"
+ "data bits: %i \n"
+ "total bits: %i (remainder: %i) \n"
+ "bitsperframe: %i \n",
+ 8 * l3_side->main_data_begin,
+ esv->ResvSize,
+ l3_side->resvDrain_post,
+ l3_side->resvDrain_pre,
+ 8 * cfg->sideinfo_len,
+ bits - l3_side->resvDrain_post - 8 * cfg->sideinfo_len,
+ bits, bits % 8, bitsPerFrame);
+
+ ERRORF(gfc, "This is a fatal error. It has several possible causes:");
+ ERRORF(gfc, "90%% LAME compiled with buggy version of gcc using advanced optimizations");
+ ERRORF(gfc, " 9%% Your system is overclocked");
+ ERRORF(gfc, " 1%% bug in LAME encoding library");
+
+ esv->ResvSize = l3_side->main_data_begin * 8;
+ };
+ assert(gfc->bs.totbit % 8 == 0);
+
+ if (gfc->bs.totbit > 1000000000) {
+ /* to avoid totbit overflow, (at 8h encoding at 128kbs) lets reset bit counter */
+ int i;
+ for (i = 0; i < MAX_HEADER_BUF; ++i)
+ esv->header[i].write_timing -= gfc->bs.totbit;
+ gfc->bs.totbit = 0;
+ }
+
+
+ return 0;
+}
+
+
+static int
+do_gain_analysis(lame_internal_flags * gfc, unsigned char* buffer, int minimum)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ RpgStateVar_t const *const rsv = &gfc->sv_rpg;
+ RpgResult_t *const rov = &gfc->ov_rpg;
+#ifdef DECODE_ON_THE_FLY
+ if (cfg->decode_on_the_fly) { /* decode the frame */
+ sample_t pcm_buf[2][1152];
+ int mp3_in = minimum;
+ int samples_out = -1;
+
+ /* re-synthesis to pcm. Repeat until we get a samples_out=0 */
+ while (samples_out != 0) {
+
+ samples_out = hip_decode1_unclipped(gfc->hip, buffer, mp3_in, pcm_buf[0], pcm_buf[1]);
+ /* samples_out = 0: need more data to decode
+ * samples_out = -1: error. Lets assume 0 pcm output
+ * samples_out = number of samples output */
+
+ /* set the lenght of the mp3 input buffer to zero, so that in the
+ * next iteration of the loop we will be querying mpglib about
+ * buffered data */
+ mp3_in = 0;
+
+ if (samples_out == -1) {
+ /* error decoding. Not fatal, but might screw up
+ * the ReplayGain tag. What should we do? Ignore for now */
+ samples_out = 0;
+ }
+ if (samples_out > 0) {
+ /* process the PCM data */
+
+ /* this should not be possible, and indicates we have
+ * overflown the pcm_buf buffer */
+ assert(samples_out <= 1152);
+
+ if (cfg->findPeakSample) {
+ int i;
+ /* FIXME: is this correct? maybe Max(fabs(pcm),PeakSample) */
+ for (i = 0; i < samples_out; i++) {
+ if (pcm_buf[0][i] > rov->PeakSample)
+ rov->PeakSample = pcm_buf[0][i];
+ else if (-pcm_buf[0][i] > rov->PeakSample)
+ rov->PeakSample = -pcm_buf[0][i];
+ }
+ if (cfg->channels_out > 1)
+ for (i = 0; i < samples_out; i++) {
+ if (pcm_buf[1][i] > rov->PeakSample)
+ rov->PeakSample = pcm_buf[1][i];
+ else if (-pcm_buf[1][i] > rov->PeakSample)
+ rov->PeakSample = -pcm_buf[1][i];
+ }
+ }
+
+ if (cfg->findReplayGain)
+ if (AnalyzeSamples
+ (rsv->rgdata, pcm_buf[0], pcm_buf[1], samples_out,
+ cfg->channels_out) == GAIN_ANALYSIS_ERROR)
+ return -6;
+
+ } /* if (samples_out>0) */
+ } /* while (samples_out!=0) */
+ } /* if (gfc->decode_on_the_fly) */
+#endif
+ return minimum;
+}
+
+static int
+do_copy_buffer(lame_internal_flags * gfc, unsigned char *buffer, int size)
+{
+ Bit_stream_struc *const bs = &gfc->bs;
+ int const minimum = bs->buf_byte_idx + 1;
+ if (minimum <= 0)
+ return 0;
+ if (size != 0 && minimum > size)
+ return -1; /* buffer is too small */
+ memcpy(buffer, bs->buf, minimum);
+ bs->buf_byte_idx = -1;
+ bs->buf_bit_idx = 0;
+ return minimum;
+}
+
+/* copy data out of the internal MP3 bit buffer into a user supplied
+ unsigned char buffer.
+
+ mp3data=0 indicates data in buffer is an id3tags and VBR tags
+ mp3data=1 data is real mp3 frame data.
+
+
+*/
+int
+copy_buffer(lame_internal_flags * gfc, unsigned char *buffer, int size, int mp3data)
+{
+ int const minimum = do_copy_buffer(gfc, buffer, size);
+ if (minimum > 0 && mp3data) {
+ UpdateMusicCRC(&gfc->nMusicCRC, buffer, minimum);
+
+ /** sum number of bytes belonging to the mp3 stream
+ * this info will be written into the Xing/LAME header for seeking
+ */
+ gfc->VBR_seek_table.nBytesWritten += minimum;
+
+ return do_gain_analysis(gfc, buffer, minimum);
+ } /* if (mp3data) */
+ return minimum;
+}
+
+
+void
+init_bit_stream_w(lame_internal_flags * gfc)
+{
+ EncStateVar_t *const esv = &gfc->sv_enc;
+
+ esv->h_ptr = esv->w_ptr = 0;
+ esv->header[esv->h_ptr].write_timing = 0;
+
+ gfc->bs.buf = (unsigned char *) malloc(BUFFER_SIZE);
+ gfc->bs.buf_size = BUFFER_SIZE;
+ gfc->bs.buf_byte_idx = -1;
+ gfc->bs.buf_bit_idx = 0;
+ gfc->bs.totbit = 0;
+}
+
+/* end of bitstream.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/bitstream.h b/libnative/src/main/cpp/module/mp3/lame/bitstream.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ae48d02946341e6233ee465ea290f945cff759e
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/bitstream.h
@@ -0,0 +1,40 @@
+/*
+ * MP3 bitstream Output interface for LAME
+ *
+ * Copyright (c) 1999 Takehiro TOMINAGA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_BITSTREAM_H
+#define LAME_BITSTREAM_H
+
+int getframebits(const lame_internal_flags * gfc);
+
+int format_bitstream(lame_internal_flags * gfc);
+
+void flush_bitstream(lame_internal_flags * gfc);
+void add_dummy_byte(lame_internal_flags * gfc, unsigned char val, unsigned int n);
+
+int copy_buffer(lame_internal_flags * gfc, unsigned char *buffer, int buffer_size,
+ int update_crc);
+void init_bit_stream_w(lame_internal_flags * gfc);
+void CRC_writeheader(lame_internal_flags const *gfc, char *buffer);
+int compute_flushbits(const lame_internal_flags * gfp, int *nbytes);
+
+int get_max_frame_buffer_size_by_constraint(SessionConfig_t const * cfg, int constraint);
+
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/depcomp b/libnative/src/main/cpp/module/mp3/lame/depcomp
new file mode 100644
index 0000000000000000000000000000000000000000..df8eea7e4ce8862105fcd7929b20bdb45488048b
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/depcomp
@@ -0,0 +1,630 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
+# Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva .
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to .
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u="sed s,\\\\\\\\,/,g"
+ depmode=msvisualcpp
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> "$depfile"
+ echo >> "$depfile"
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add `dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mechanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/libnative/src/main/cpp/module/mp3/lame/encoder.c b/libnative/src/main/cpp/module/mp3/lame/encoder.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d5b0b624dcdd6884bd261804e4f0330fc6c08ad
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/encoder.c
@@ -0,0 +1,557 @@
+/*
+ * LAME MP3 encoding engine
+ *
+ * Copyright (c) 1999 Mark Taylor
+ * Copyright (c) 2000-2002 Takehiro Tominaga
+ * Copyright (c) 2000-2011 Robert Hegemann
+ * Copyright (c) 2001 Gabriel Bouvigne
+ * Copyright (c) 2001 John Dahlstrom
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: encoder.c,v 1.111 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "lame_global_flags.h"
+#include "newmdct.h"
+#include "psymodel.h"
+#include "lame-analysis.h"
+#include "bitstream.h"
+#include "VbrTag.h"
+#include "quantize_pvt.h"
+
+
+
+/*
+ * auto-adjust of ATH, useful for low volume
+ * Gabriel Bouvigne 3 feb 2001
+ *
+ * modifies some values in
+ * gfp->internal_flags->ATH
+ * (gfc->ATH)
+ */
+static void
+adjust_ATH(lame_internal_flags const *const gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ FLOAT gr2_max, max_pow;
+
+ if (gfc->ATH->use_adjust == 0) {
+ gfc->ATH->adjust_factor = 1.0; /* no adjustment */
+ return;
+ }
+
+ /* jd - 2001 mar 12, 27, jun 30 */
+ /* loudness based on equal loudness curve; */
+ /* use granule with maximum combined loudness */
+ max_pow = gfc->ov_psy.loudness_sq[0][0];
+ gr2_max = gfc->ov_psy.loudness_sq[1][0];
+ if (cfg->channels_out == 2) {
+ max_pow += gfc->ov_psy.loudness_sq[0][1];
+ gr2_max += gfc->ov_psy.loudness_sq[1][1];
+ }
+ else {
+ max_pow += max_pow;
+ gr2_max += gr2_max;
+ }
+ if (cfg->mode_gr == 2) {
+ max_pow = Max(max_pow, gr2_max);
+ }
+ max_pow *= 0.5; /* max_pow approaches 1.0 for full band noise */
+
+ /* jd - 2001 mar 31, jun 30 */
+ /* user tuning of ATH adjustment region */
+ max_pow *= gfc->ATH->aa_sensitivity_p;
+
+ /* adjust ATH depending on range of maximum value
+ */
+
+ /* jd - 2001 feb27, mar12,20, jun30, jul22 */
+ /* continuous curves based on approximation */
+ /* to GB's original values. */
+ /* For an increase in approximate loudness, */
+ /* set ATH adjust to adjust_limit immediately */
+ /* after a delay of one frame. */
+ /* For a loudness decrease, reduce ATH adjust */
+ /* towards adjust_limit gradually. */
+ /* max_pow is a loudness squared or a power. */
+ if (max_pow > 0.03125) { /* ((1 - 0.000625)/ 31.98) from curve below */
+ if (gfc->ATH->adjust_factor >= 1.0) {
+ gfc->ATH->adjust_factor = 1.0;
+ }
+ else {
+ /* preceding frame has lower ATH adjust; */
+ /* ascend only to the preceding adjust_limit */
+ /* in case there is leading low volume */
+ if (gfc->ATH->adjust_factor < gfc->ATH->adjust_limit) {
+ gfc->ATH->adjust_factor = gfc->ATH->adjust_limit;
+ }
+ }
+ gfc->ATH->adjust_limit = 1.0;
+ }
+ else { /* adjustment curve */
+ /* about 32 dB maximum adjust (0.000625) */
+ FLOAT const adj_lim_new = 31.98 * max_pow + 0.000625;
+ if (gfc->ATH->adjust_factor >= adj_lim_new) { /* descend gradually */
+ gfc->ATH->adjust_factor *= adj_lim_new * 0.075 + 0.925;
+ if (gfc->ATH->adjust_factor < adj_lim_new) { /* stop descent */
+ gfc->ATH->adjust_factor = adj_lim_new;
+ }
+ }
+ else { /* ascend */
+ if (gfc->ATH->adjust_limit >= adj_lim_new) {
+ gfc->ATH->adjust_factor = adj_lim_new;
+ }
+ else { /* preceding frame has lower ATH adjust; */
+ /* ascend only to the preceding adjust_limit */
+ if (gfc->ATH->adjust_factor < gfc->ATH->adjust_limit) {
+ gfc->ATH->adjust_factor = gfc->ATH->adjust_limit;
+ }
+ }
+ }
+ gfc->ATH->adjust_limit = adj_lim_new;
+ }
+}
+
+/***********************************************************************
+ *
+ * some simple statistics
+ *
+ * bitrate index 0: free bitrate -> not allowed in VBR mode
+ * : bitrates, kbps depending on MPEG version
+ * bitrate index 15: forbidden
+ *
+ * mode_ext:
+ * 0: LR
+ * 1: LR-i
+ * 2: MS
+ * 3: MS-i
+ *
+ ***********************************************************************/
+
+static void
+updateStats(lame_internal_flags * const gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *eov = &gfc->ov_enc;
+ int gr, ch;
+ assert(0 <= eov->bitrate_index && eov->bitrate_index < 16);
+ assert(0 <= eov->mode_ext && eov->mode_ext < 4);
+
+ /* count bitrate indices */
+ eov->bitrate_channelmode_hist[eov->bitrate_index][4]++;
+ eov->bitrate_channelmode_hist[15][4]++;
+
+ /* count 'em for every mode extension in case of 2 channel encoding */
+ if (cfg->channels_out == 2) {
+ eov->bitrate_channelmode_hist[eov->bitrate_index][eov->mode_ext]++;
+ eov->bitrate_channelmode_hist[15][eov->mode_ext]++;
+ }
+ for (gr = 0; gr < cfg->mode_gr; ++gr) {
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ int bt = gfc->l3_side.tt[gr][ch].block_type;
+ if (gfc->l3_side.tt[gr][ch].mixed_block_flag)
+ bt = 4;
+ eov->bitrate_blocktype_hist[eov->bitrate_index][bt]++;
+ eov->bitrate_blocktype_hist[eov->bitrate_index][5]++;
+ eov->bitrate_blocktype_hist[15][bt]++;
+ eov->bitrate_blocktype_hist[15][5]++;
+ }
+ }
+}
+
+
+
+
+static void
+lame_encode_frame_init(lame_internal_flags * gfc, const sample_t *const inbuf[2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+
+ int ch, gr;
+
+ if (gfc->lame_encode_frame_init == 0) {
+ sample_t primebuff0[286 + 1152 + 576];
+ sample_t primebuff1[286 + 1152 + 576];
+ int const framesize = 576 * cfg->mode_gr;
+ /* prime the MDCT/polyphase filterbank with a short block */
+ int i, j;
+ gfc->lame_encode_frame_init = 1;
+ memset(primebuff0, 0, sizeof(primebuff0));
+ memset(primebuff1, 0, sizeof(primebuff1));
+ for (i = 0, j = 0; i < 286 + 576 * (1 + cfg->mode_gr); ++i) {
+ if (i < framesize) {
+ primebuff0[i] = 0;
+ if (cfg->channels_out == 2)
+ primebuff1[i] = 0;
+ }
+ else {
+ primebuff0[i] = inbuf[0][j];
+ if (cfg->channels_out == 2)
+ primebuff1[i] = inbuf[1][j];
+ ++j;
+ }
+ }
+ /* polyphase filtering / mdct */
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gfc->l3_side.tt[gr][ch].block_type = SHORT_TYPE;
+ }
+ }
+ mdct_sub48(gfc, primebuff0, primebuff1);
+
+ /* check FFT will not use a negative starting offset */
+#if 576 < FFTOFFSET
+# error FFTOFFSET greater than 576: FFT uses a negative offset
+#endif
+ /* check if we have enough data for FFT */
+ assert(gfc->sv_enc.mf_size >= (BLKSIZE + framesize - FFTOFFSET));
+ /* check if we have enough data for polyphase filterbank */
+ assert(gfc->sv_enc.mf_size >= (512 + framesize - 32));
+ }
+
+}
+
+
+
+
+
+
+
+/************************************************************************
+*
+* encodeframe() Layer 3
+*
+* encode a single frame
+*
+************************************************************************
+lame_encode_frame()
+
+
+ gr 0 gr 1
+inbuf: |--------------|--------------|--------------|
+
+
+Polyphase (18 windows, each shifted 32)
+gr 0:
+window1 <----512---->
+window18 <----512---->
+
+gr 1:
+window1 <----512---->
+window18 <----512---->
+
+
+
+MDCT output: |--------------|--------------|--------------|
+
+FFT's <---------1024---------->
+ <---------1024-------->
+
+
+
+ inbuf = buffer of PCM data size=MP3 framesize
+ encoder acts on inbuf[ch][0], but output is delayed by MDCTDELAY
+ so the MDCT coefficints are from inbuf[ch][-MDCTDELAY]
+
+ psy-model FFT has a 1 granule delay, so we feed it data for the
+ next granule.
+ FFT is centered over granule: 224+576+224
+ So FFT starts at: 576-224-MDCTDELAY
+
+ MPEG2: FFT ends at: BLKSIZE+576-224-MDCTDELAY (1328)
+ MPEG1: FFT ends at: BLKSIZE+2*576-224-MDCTDELAY (1904)
+
+ MPEG2: polyphase first window: [0..511]
+ 18th window: [544..1055] (1056)
+ MPEG1: 36th window: [1120..1631] (1632)
+ data needed: 512+framesize-32
+
+ A close look newmdct.c shows that the polyphase filterbank
+ only uses data from [0..510] for each window. Perhaps because the window
+ used by the filterbank is zero for the last point, so Takehiro's
+ code doesn't bother to compute with it.
+
+ FFT starts at 576-224-MDCTDELAY (304) = 576-FFTOFFSET
+
+*/
+
+typedef FLOAT chgrdata[2][2];
+
+
+int
+lame_encode_mp3_frame( /* Output */
+ lame_internal_flags * gfc, /* Context */
+ sample_t const *inbuf_l, /* Input */
+ sample_t const *inbuf_r, /* Input */
+ unsigned char *mp3buf, /* Output */
+ int mp3buf_size)
+{ /* Output */
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int mp3count;
+ III_psy_ratio masking_LR[2][2]; /*LR masking & energy */
+ III_psy_ratio masking_MS[2][2]; /*MS masking & energy */
+ const III_psy_ratio (*masking)[2]; /*pointer to selected maskings */
+ const sample_t *inbuf[2];
+
+ FLOAT tot_ener[2][4];
+ FLOAT ms_ener_ratio[2] = { .5, .5 };
+ FLOAT pe[2][2] = { {0., 0.}, {0., 0.} }, pe_MS[2][2] = { {
+ 0., 0.}, {
+ 0., 0.}};
+ FLOAT (*pe_use)[2];
+
+ int ch, gr;
+
+ inbuf[0] = inbuf_l;
+ inbuf[1] = inbuf_r;
+
+ if (gfc->lame_encode_frame_init == 0) {
+ /*first run? */
+ lame_encode_frame_init(gfc, inbuf);
+
+ }
+
+
+ /********************** padding *****************************/
+ /* padding method as described in
+ * "MPEG-Layer3 / Bitstream Syntax and Decoding"
+ * by Martin Sieler, Ralph Sperschneider
+ *
+ * note: there is no padding for the very first frame
+ *
+ * Robert Hegemann 2000-06-22
+ */
+ gfc->ov_enc.padding = FALSE;
+ if ((gfc->sv_enc.slot_lag -= gfc->sv_enc.frac_SpF) < 0) {
+ gfc->sv_enc.slot_lag += cfg->samplerate_out;
+ gfc->ov_enc.padding = TRUE;
+ }
+
+
+
+ /****************************************
+ * Stage 1: psychoacoustic model *
+ ****************************************/
+
+ {
+ /* psychoacoustic model
+ * psy model has a 1 granule (576) delay that we must compensate for
+ * (mt 6/99).
+ */
+ int ret;
+ const sample_t *bufp[2] = {0, 0}; /* address of beginning of left & right granule */
+ int blocktype[2];
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ bufp[ch] = &inbuf[ch][576 + gr * 576 - FFTOFFSET];
+ }
+ ret = L3psycho_anal_vbr(gfc, bufp, gr,
+ masking_LR, masking_MS,
+ pe[gr], pe_MS[gr], tot_ener[gr], blocktype);
+ if (ret != 0)
+ return -4;
+
+ if (cfg->mode == JOINT_STEREO) {
+ ms_ener_ratio[gr] = tot_ener[gr][2] + tot_ener[gr][3];
+ if (ms_ener_ratio[gr] > 0)
+ ms_ener_ratio[gr] = tot_ener[gr][3] / ms_ener_ratio[gr];
+ }
+
+ /* block type flags */
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info *const cod_info = &gfc->l3_side.tt[gr][ch];
+ cod_info->block_type = blocktype[ch];
+ cod_info->mixed_block_flag = 0;
+ }
+ }
+ }
+
+
+ /* auto-adjust of ATH, useful for low volume */
+ adjust_ATH(gfc);
+
+
+ /****************************************
+ * Stage 2: MDCT *
+ ****************************************/
+
+ /* polyphase filtering / mdct */
+ mdct_sub48(gfc, inbuf[0], inbuf[1]);
+
+
+ /****************************************
+ * Stage 3: MS/LR decision *
+ ****************************************/
+
+ /* Here will be selected MS or LR coding of the 2 stereo channels */
+ gfc->ov_enc.mode_ext = MPG_MD_LR_LR;
+
+ if (cfg->force_ms) {
+ gfc->ov_enc.mode_ext = MPG_MD_MS_LR;
+ }
+ else if (cfg->mode == JOINT_STEREO) {
+ /* ms_ratio = is scaled, for historical reasons, to look like
+ a ratio of side_channel / total.
+ 0 = signal is 100% mono
+ .5 = L & R uncorrelated
+ */
+
+ /* [0] and [1] are the results for the two granules in MPEG-1,
+ * in MPEG-2 it's only a faked averaging of the same value
+ * _prev is the value of the last granule of the previous frame
+ * _next is the value of the first granule of the next frame
+ */
+
+ FLOAT sum_pe_MS = 0;
+ FLOAT sum_pe_LR = 0;
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ sum_pe_MS += pe_MS[gr][ch];
+ sum_pe_LR += pe[gr][ch];
+ }
+ }
+
+ /* based on PE: M/S coding would not use much more bits than L/R */
+ if (sum_pe_MS <= 1.00 * sum_pe_LR) {
+
+ gr_info const *const gi0 = &gfc->l3_side.tt[0][0];
+ gr_info const *const gi1 = &gfc->l3_side.tt[cfg->mode_gr - 1][0];
+
+ if (gi0[0].block_type == gi0[1].block_type && gi1[0].block_type == gi1[1].block_type) {
+
+ gfc->ov_enc.mode_ext = MPG_MD_MS_LR;
+ }
+ }
+ }
+
+ /* bit and noise allocation */
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ masking = (const III_psy_ratio (*)[2])masking_MS; /* use MS masking */
+ pe_use = pe_MS;
+ }
+ else {
+ masking = (const III_psy_ratio (*)[2])masking_LR; /* use LR masking */
+ pe_use = pe;
+ }
+
+
+ /* copy data for MP3 frame analyzer */
+ if (cfg->analysis && gfc->pinfo != NULL) {
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gfc->pinfo->ms_ratio[gr] = 0;
+ gfc->pinfo->ms_ener_ratio[gr] = ms_ener_ratio[gr];
+ gfc->pinfo->blocktype[gr][ch] = gfc->l3_side.tt[gr][ch].block_type;
+ gfc->pinfo->pe[gr][ch] = pe_use[gr][ch];
+ memcpy(gfc->pinfo->xr[gr][ch], &gfc->l3_side.tt[gr][ch].xr[0], sizeof(FLOAT) * 576);
+ /* in psymodel, LR and MS data was stored in pinfo.
+ switch to MS data: */
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ gfc->pinfo->ers[gr][ch] = gfc->pinfo->ers[gr][ch + 2];
+ memcpy(gfc->pinfo->energy[gr][ch], gfc->pinfo->energy[gr][ch + 2],
+ sizeof(gfc->pinfo->energy[gr][ch]));
+ }
+ }
+ }
+ }
+
+
+ /****************************************
+ * Stage 4: quantization loop *
+ ****************************************/
+
+ if (cfg->vbr == vbr_off || cfg->vbr == vbr_abr) {
+ static FLOAT const fircoef[9] = {
+ -0.0207887 * 5, -0.0378413 * 5, -0.0432472 * 5, -0.031183 * 5,
+ 7.79609e-18 * 5, 0.0467745 * 5, 0.10091 * 5, 0.151365 * 5,
+ 0.187098 * 5
+ };
+
+ int i;
+ FLOAT f;
+
+ for (i = 0; i < 18; i++)
+ gfc->sv_enc.pefirbuf[i] = gfc->sv_enc.pefirbuf[i + 1];
+
+ f = 0.0;
+ for (gr = 0; gr < cfg->mode_gr; gr++)
+ for (ch = 0; ch < cfg->channels_out; ch++)
+ f += pe_use[gr][ch];
+ gfc->sv_enc.pefirbuf[18] = f;
+
+ f = gfc->sv_enc.pefirbuf[9];
+ for (i = 0; i < 9; i++)
+ f += (gfc->sv_enc.pefirbuf[i] + gfc->sv_enc.pefirbuf[18 - i]) * fircoef[i];
+
+ f = (670 * 5 * cfg->mode_gr * cfg->channels_out) / f;
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ pe_use[gr][ch] *= f;
+ }
+ }
+ }
+ gfc->iteration_loop(gfc, (const FLOAT (*)[2])pe_use, ms_ener_ratio, masking);
+
+
+ /****************************************
+ * Stage 5: bitstream formatting *
+ ****************************************/
+
+
+ /* write the frame to the bitstream */
+ (void) format_bitstream(gfc);
+
+ /* copy mp3 bit buffer into array */
+ mp3count = copy_buffer(gfc, mp3buf, mp3buf_size, 1);
+
+
+ if (cfg->write_lame_tag) {
+ AddVbrFrame(gfc);
+ }
+
+ if (cfg->analysis && gfc->pinfo != NULL) {
+ int framesize = 576 * cfg->mode_gr;
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ int j;
+ for (j = 0; j < FFTOFFSET; j++)
+ gfc->pinfo->pcmdata[ch][j] = gfc->pinfo->pcmdata[ch][j + framesize];
+ for (j = FFTOFFSET; j < 1600; j++) {
+ gfc->pinfo->pcmdata[ch][j] = inbuf[ch][j - FFTOFFSET];
+ }
+ }
+ gfc->sv_qnt.masking_lower = 1.0;
+
+ set_frame_pinfo(gfc, masking);
+ }
+
+ ++gfc->ov_enc.frame_number;
+
+ updateStats(gfc);
+
+ return mp3count;
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/encoder.h b/libnative/src/main/cpp/module/mp3/lame/encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..b06a7c6444a2a989a3076266daf7e07c3adb6da4
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/encoder.h
@@ -0,0 +1,156 @@
+/*
+ * encoder.h include file
+ *
+ * Copyright (c) 2000 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef LAME_ENCODER_H
+#define LAME_ENCODER_H
+
+/***********************************************************************
+*
+* encoder and decoder delays
+*
+***********************************************************************/
+
+/*
+ * layer III enc->dec delay: 1056 (1057?) (observed)
+ * layer II enc->dec delay: 480 (481?) (observed)
+ *
+ * polyphase 256-16 (dec or enc) = 240
+ * mdct 256+32 (9*32) (dec or enc) = 288
+ * total: 512+16
+ *
+ * My guess is that delay of polyphase filterbank is actualy 240.5
+ * (there are technical reasons for this, see postings in mp3encoder).
+ * So total Encode+Decode delay = ENCDELAY + 528 + 1
+ */
+
+/*
+ * ENCDELAY The encoder delay.
+ *
+ * Minimum allowed is MDCTDELAY (see below)
+ *
+ * The first 96 samples will be attenuated, so using a value less than 96
+ * will result in corrupt data for the first 96-ENCDELAY samples.
+ *
+ * suggested: 576
+ * set to 1160 to sync with FhG.
+ */
+
+#define ENCDELAY 576
+
+
+
+/*
+ * make sure there is at least one complete frame after the
+ * last frame containing real data
+ *
+ * Using a value of 288 would be sufficient for a
+ * a very sophisticated decoder that can decode granule-by-granule instead
+ * of frame by frame. But lets not assume this, and assume the decoder
+ * will not decode frame N unless it also has data for frame N+1
+ *
+ */
+/*#define POSTDELAY 288*/
+#define POSTDELAY 1152
+
+
+
+/*
+ * delay of the MDCT used in mdct.c
+ * original ISO routines had a delay of 528!
+ * Takehiro's routines:
+ */
+
+#define MDCTDELAY 48
+#define FFTOFFSET (224+MDCTDELAY)
+
+/*
+ * Most decoders, including the one we use, have a delay of 528 samples.
+ */
+
+#define DECDELAY 528
+
+
+/* number of subbands */
+#define SBLIMIT 32
+
+/* parition bands bands */
+#define CBANDS 64
+
+/* number of critical bands/scale factor bands where masking is computed*/
+#define SBPSY_l 21
+#define SBPSY_s 12
+
+/* total number of scalefactor bands encoded */
+#define SBMAX_l 22
+#define SBMAX_s 13
+#define PSFB21 6
+#define PSFB12 6
+
+
+
+/* FFT sizes */
+#define BLKSIZE 1024
+#define HBLKSIZE (BLKSIZE/2 + 1)
+#define BLKSIZE_s 256
+#define HBLKSIZE_s (BLKSIZE_s/2 + 1)
+
+
+/* #define switch_pe 1800 */
+#define NORM_TYPE 0
+#define START_TYPE 1
+#define SHORT_TYPE 2
+#define STOP_TYPE 3
+
+/*
+ * Mode Extention:
+ * When we are in stereo mode, there are 4 possible methods to store these
+ * two channels. The stereo modes -m? are using a subset of them.
+ *
+ * -ms: MPG_MD_LR_LR
+ * -mj: MPG_MD_LR_LR and MPG_MD_MS_LR
+ * -mf: MPG_MD_MS_LR
+ * -mi: all
+ */
+#if 0
+#define MPG_MD_LR_LR 0
+#define MPG_MD_LR_I 1
+#define MPG_MD_MS_LR 2
+#define MPG_MD_MS_I 3
+#endif
+enum MPEGChannelMode
+{ MPG_MD_LR_LR = 0
+, MPG_MD_LR_I = 1
+, MPG_MD_MS_LR = 2
+, MPG_MD_MS_I = 3
+};
+
+#ifndef lame_internal_flags_defined
+#define lame_internal_flags_defined
+struct lame_internal_flags;
+typedef struct lame_internal_flags lame_internal_flags;
+#endif
+
+int lame_encode_mp3_frame(lame_internal_flags * gfc,
+ sample_t const *inbuf_l,
+ sample_t const *inbuf_r, unsigned char *mp3buf, int mp3buf_size);
+
+#endif /* LAME_ENCODER_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/fft.c b/libnative/src/main/cpp/module/mp3/lame/fft.c
new file mode 100644
index 0000000000000000000000000000000000000000..5bfd3fcafe6d086e6f3a032534a5e2190dfe005a
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/fft.c
@@ -0,0 +1,329 @@
+/*
+** FFT and FHT routines
+** Copyright 1988, 1993; Ron Mayer
+** Copyright (c) 1999-2000 Takehiro Tominaga
+**
+** fht(fz,n);
+** Does a hartley transform of "n" points in the array "fz".
+**
+** NOTE: This routine uses at least 2 patented algorithms, and may be
+** under the restrictions of a bunch of different organizations.
+** Although I wrote it completely myself; it is kind of a derivative
+** of a routine I once authored and released under the GPL, so it
+** may fall under the free software foundation's restrictions;
+** it was worked on as a Stanford Univ project, so they claim
+** some rights to it; it was further optimized at work here, so
+** I think this company claims parts of it. The patents are
+** held by R. Bracewell (the FHT algorithm) and O. Buneman (the
+** trig generator), both at Stanford Univ.
+** If it were up to me, I'd say go do whatever you want with it;
+** but it would be polite to give credit to the following people
+** if you use this anywhere:
+** Euler - probable inventor of the fourier transform.
+** Gauss - probable inventor of the FFT.
+** Hartley - probable inventor of the hartley transform.
+** Buneman - for a really cool trig generator
+** Mayer(me) - for authoring this particular version and
+** including all the optimizations in one package.
+** Thanks,
+** Ron Mayer; mayer@acuson.com
+** and added some optimization by
+** Mather - idea of using lookup table
+** Takehiro - some dirty hack for speed up
+*/
+
+/* $Id: fft.c,v 1.38 2009/04/20 21:48:00 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "fft.h"
+
+//#include "vector/lame_intrin.h"
+
+
+
+#define TRI_SIZE (5-1) /* 1024 = 4**5 */
+
+/* fft.c */
+static FLOAT window[BLKSIZE], window_s[BLKSIZE_s / 2];
+
+static const FLOAT costab[TRI_SIZE * 2] = {
+ 9.238795325112867e-01, 3.826834323650898e-01,
+ 9.951847266721969e-01, 9.801714032956060e-02,
+ 9.996988186962042e-01, 2.454122852291229e-02,
+ 9.999811752826011e-01, 6.135884649154475e-03
+};
+
+static void
+fht(FLOAT * fz, int n)
+{
+ const FLOAT *tri = costab;
+ int k4;
+ FLOAT *fi, *gi;
+ FLOAT const *fn;
+
+ n <<= 1; /* to get BLKSIZE, because of 3DNow! ASM routine */
+ fn = fz + n;
+ k4 = 4;
+ do {
+ FLOAT s1, c1;
+ int i, k1, k2, k3, kx;
+ kx = k4 >> 1;
+ k1 = k4;
+ k2 = k4 << 1;
+ k3 = k2 + k1;
+ k4 = k2 << 1;
+ fi = fz;
+ gi = fi + kx;
+ do {
+ FLOAT f0, f1, f2, f3;
+ f1 = fi[0] - fi[k1];
+ f0 = fi[0] + fi[k1];
+ f3 = fi[k2] - fi[k3];
+ f2 = fi[k2] + fi[k3];
+ fi[k2] = f0 - f2;
+ fi[0] = f0 + f2;
+ fi[k3] = f1 - f3;
+ fi[k1] = f1 + f3;
+ f1 = gi[0] - gi[k1];
+ f0 = gi[0] + gi[k1];
+ f3 = SQRT2 * gi[k3];
+ f2 = SQRT2 * gi[k2];
+ gi[k2] = f0 - f2;
+ gi[0] = f0 + f2;
+ gi[k3] = f1 - f3;
+ gi[k1] = f1 + f3;
+ gi += k4;
+ fi += k4;
+ } while (fi < fn);
+ c1 = tri[0];
+ s1 = tri[1];
+ for (i = 1; i < kx; i++) {
+ FLOAT c2, s2;
+ c2 = 1 - (2 * s1) * s1;
+ s2 = (2 * s1) * c1;
+ fi = fz + i;
+ gi = fz + k1 - i;
+ do {
+ FLOAT a, b, g0, f0, f1, g1, f2, g2, f3, g3;
+ b = s2 * fi[k1] - c2 * gi[k1];
+ a = c2 * fi[k1] + s2 * gi[k1];
+ f1 = fi[0] - a;
+ f0 = fi[0] + a;
+ g1 = gi[0] - b;
+ g0 = gi[0] + b;
+ b = s2 * fi[k3] - c2 * gi[k3];
+ a = c2 * fi[k3] + s2 * gi[k3];
+ f3 = fi[k2] - a;
+ f2 = fi[k2] + a;
+ g3 = gi[k2] - b;
+ g2 = gi[k2] + b;
+ b = s1 * f2 - c1 * g3;
+ a = c1 * f2 + s1 * g3;
+ fi[k2] = f0 - a;
+ fi[0] = f0 + a;
+ gi[k3] = g1 - b;
+ gi[k1] = g1 + b;
+ b = c1 * g2 - s1 * f3;
+ a = s1 * g2 + c1 * f3;
+ gi[k2] = g0 - a;
+ gi[0] = g0 + a;
+ fi[k3] = f1 - b;
+ fi[k1] = f1 + b;
+ gi += k4;
+ fi += k4;
+ } while (fi < fn);
+ c2 = c1;
+ c1 = c2 * tri[0] - s1 * tri[1];
+ s1 = c2 * tri[1] + s1 * tri[0];
+ }
+ tri += 2;
+ } while (k4 < n);
+}
+
+
+static const unsigned char rv_tbl[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe
+};
+
+#define ch01(index) (buffer[chn][index])
+
+#define ml00(f) (window[i ] * f(i))
+#define ml10(f) (window[i + 0x200] * f(i + 0x200))
+#define ml20(f) (window[i + 0x100] * f(i + 0x100))
+#define ml30(f) (window[i + 0x300] * f(i + 0x300))
+
+#define ml01(f) (window[i + 0x001] * f(i + 0x001))
+#define ml11(f) (window[i + 0x201] * f(i + 0x201))
+#define ml21(f) (window[i + 0x101] * f(i + 0x101))
+#define ml31(f) (window[i + 0x301] * f(i + 0x301))
+
+#define ms00(f) (window_s[i ] * f(i + k))
+#define ms10(f) (window_s[0x7f - i] * f(i + k + 0x80))
+#define ms20(f) (window_s[i + 0x40] * f(i + k + 0x40))
+#define ms30(f) (window_s[0x3f - i] * f(i + k + 0xc0))
+
+#define ms01(f) (window_s[i + 0x01] * f(i + k + 0x01))
+#define ms11(f) (window_s[0x7e - i] * f(i + k + 0x81))
+#define ms21(f) (window_s[i + 0x41] * f(i + k + 0x41))
+#define ms31(f) (window_s[0x3e - i] * f(i + k + 0xc1))
+
+
+void
+fft_short(lame_internal_flags const *const gfc,
+ FLOAT x_real[3][BLKSIZE_s], int chn, const sample_t *const buffer[2])
+{
+ int i;
+ int j;
+ int b;
+
+ for (b = 0; b < 3; b++) {
+ FLOAT *x = &x_real[b][BLKSIZE_s / 2];
+ short const k = (576 / 3) * (b + 1);
+ j = BLKSIZE_s / 8 - 1;
+ do {
+ FLOAT f0, f1, f2, f3, w;
+
+ i = rv_tbl[j << 2];
+
+ f0 = ms00(ch01);
+ w = ms10(ch01);
+ f1 = f0 - w;
+ f0 = f0 + w;
+ f2 = ms20(ch01);
+ w = ms30(ch01);
+ f3 = f2 - w;
+ f2 = f2 + w;
+
+ x -= 4;
+ x[0] = f0 + f2;
+ x[2] = f0 - f2;
+ x[1] = f1 + f3;
+ x[3] = f1 - f3;
+
+ f0 = ms01(ch01);
+ w = ms11(ch01);
+ f1 = f0 - w;
+ f0 = f0 + w;
+ f2 = ms21(ch01);
+ w = ms31(ch01);
+ f3 = f2 - w;
+ f2 = f2 + w;
+
+ x[BLKSIZE_s / 2 + 0] = f0 + f2;
+ x[BLKSIZE_s / 2 + 2] = f0 - f2;
+ x[BLKSIZE_s / 2 + 1] = f1 + f3;
+ x[BLKSIZE_s / 2 + 3] = f1 - f3;
+ } while (--j >= 0);
+
+ gfc->fft_fht(x, BLKSIZE_s / 2);
+ /* BLKSIZE_s/2 because of 3DNow! ASM routine */
+ }
+}
+
+void
+fft_long(lame_internal_flags const *const gfc,
+ FLOAT x[BLKSIZE], int chn, const sample_t *const buffer[2])
+{
+ int i;
+ int jj = BLKSIZE / 8 - 1;
+ x += BLKSIZE / 2;
+
+ do {
+ FLOAT f0, f1, f2, f3, w;
+
+ i = rv_tbl[jj];
+ f0 = ml00(ch01);
+ w = ml10(ch01);
+ f1 = f0 - w;
+ f0 = f0 + w;
+ f2 = ml20(ch01);
+ w = ml30(ch01);
+ f3 = f2 - w;
+ f2 = f2 + w;
+
+ x -= 4;
+ x[0] = f0 + f2;
+ x[2] = f0 - f2;
+ x[1] = f1 + f3;
+ x[3] = f1 - f3;
+
+ f0 = ml01(ch01);
+ w = ml11(ch01);
+ f1 = f0 - w;
+ f0 = f0 + w;
+ f2 = ml21(ch01);
+ w = ml31(ch01);
+ f3 = f2 - w;
+ f2 = f2 + w;
+
+ x[BLKSIZE / 2 + 0] = f0 + f2;
+ x[BLKSIZE / 2 + 2] = f0 - f2;
+ x[BLKSIZE / 2 + 1] = f1 + f3;
+ x[BLKSIZE / 2 + 3] = f1 - f3;
+ } while (--jj >= 0);
+
+ gfc->fft_fht(x, BLKSIZE / 2);
+ /* BLKSIZE/2 because of 3DNow! ASM routine */
+}
+
+#ifdef HAVE_NASM
+extern void fht_3DN(FLOAT * fz, int n);
+extern void fht_SSE(FLOAT * fz, int n);
+#endif
+
+void
+init_fft(lame_internal_flags * const gfc)
+{
+ int i;
+
+ /* The type of window used here will make no real difference, but */
+ /* in the interest of merging nspsytune stuff - switch to blackman window */
+ for (i = 0; i < BLKSIZE; i++)
+ /* blackman window */
+ window[i] = 0.42 - 0.5 * cos(2 * PI * (i + .5) / BLKSIZE) +
+ 0.08 * cos(4 * PI * (i + .5) / BLKSIZE);
+
+ for (i = 0; i < BLKSIZE_s / 2; i++)
+ window_s[i] = 0.5 * (1.0 - cos(2.0 * PI * (i + 0.5) / BLKSIZE_s));
+
+ gfc->fft_fht = fht;
+#ifdef HAVE_NASM
+ if (gfc->CPU_features.AMD_3DNow) {
+ gfc->fft_fht = fht_3DN;
+ }
+ else if (gfc->CPU_features.SSE) {
+ gfc->fft_fht = fht_SSE;
+ }
+ else {
+ gfc->fft_fht = fht;
+ }
+#else
+#ifdef HAVE_XMMINTRIN_H
+#ifdef MIN_ARCH_SSE
+ gfc->fft_fht = fht_SSE2;
+#endif
+#endif
+#endif
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/fft.h b/libnative/src/main/cpp/module/mp3/lame/fft.h
new file mode 100644
index 0000000000000000000000000000000000000000..258df88703303b2cc8ac527768f9462f90f82cd3
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/fft.h
@@ -0,0 +1,35 @@
+/*
+ * Fast Fourier Transform include file
+ *
+ * Copyright (c) 2000 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_FFT_H
+#define LAME_FFT_H
+
+void fft_long(lame_internal_flags const *const gfc, FLOAT x_real[BLKSIZE],
+ int chn, const sample_t *const data[2]);
+
+void fft_short(lame_internal_flags const *const gfc, FLOAT x_real[3][BLKSIZE_s],
+ int chn, const sample_t *const data[2]);
+
+void init_fft(lame_internal_flags * const gfc);
+
+#endif
+
+/* End of fft.h */
diff --git a/libnative/src/main/cpp/module/mp3/lame/gain_analysis.c b/libnative/src/main/cpp/module/mp3/lame/gain_analysis.c
new file mode 100644
index 0000000000000000000000000000000000000000..145458b425ec07948bb520d7c846a91776528762
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/gain_analysis.c
@@ -0,0 +1,518 @@
+/*
+ * ReplayGainAnalysis - analyzes input samples and give the recommended dB change
+ * Copyright (C) 2001 David Robinson and Glen Sawyer
+ * Improvements and optimizations added by Frank Klemm, and by Marcel Muller
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * concept and filter values by David Robinson (David@Robinson.org)
+ * -- blame him if you think the idea is flawed
+ * original coding by Glen Sawyer (mp3gain@hotmail.com)
+ * -- blame him if you think this runs too slowly, or the coding is otherwise flawed
+ *
+ * lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ )
+ * -- credit him for all the _good_ programming ;)
+ *
+ *
+ * For an explanation of the concepts and the basic algorithms involved, go to:
+ * http://www.replaygain.org/
+ */
+
+/*
+ * Here's the deal. Call
+ *
+ * InitGainAnalysis ( long samplefreq );
+ *
+ * to initialize everything. Call
+ *
+ * AnalyzeSamples ( const Float_t* left_samples,
+ * const Float_t* right_samples,
+ * size_t num_samples,
+ * int num_channels );
+ *
+ * as many times as you want, with as many or as few samples as you want.
+ * If mono, pass the sample buffer in through left_samples, leave
+ * right_samples NULL, and make sure num_channels = 1.
+ *
+ * GetTitleGain()
+ *
+ * will return the recommended dB level change for all samples analyzed
+ * SINCE THE LAST TIME you called GetTitleGain() OR InitGainAnalysis().
+ *
+ * GetAlbumGain()
+ *
+ * will return the recommended dB level change for all samples analyzed
+ * since InitGainAnalysis() was called and finalized with GetTitleGain().
+ *
+ * Pseudo-code to process an album:
+ *
+ * Float_t l_samples [4096];
+ * Float_t r_samples [4096];
+ * size_t num_samples;
+ * unsigned int num_songs;
+ * unsigned int i;
+ *
+ * InitGainAnalysis ( 44100 );
+ * for ( i = 1; i <= num_songs; i++ ) {
+ * while ( ( num_samples = getSongSamples ( song[i], left_samples, right_samples ) ) > 0 )
+ * AnalyzeSamples ( left_samples, right_samples, num_samples, 2 );
+ * fprintf ("Recommended dB change for song %2d: %+6.2f dB\n", i, GetTitleGain() );
+ * }
+ * fprintf ("Recommended dB change for whole album: %+6.2f dB\n", GetAlbumGain() );
+ */
+
+/*
+ * So here's the main source of potential code confusion:
+ *
+ * The filters applied to the incoming samples are IIR filters,
+ * meaning they rely on up to number of previous samples
+ * AND up to number of previous filtered samples.
+ *
+ * I set up the AnalyzeSamples routine to minimize memory usage and interface
+ * complexity. The speed isn't compromised too much (I don't think), but the
+ * internal complexity is higher than it should be for such a relatively
+ * simple routine.
+ *
+ * Optimization/clarity suggestions are welcome.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+#include
+
+#include "lame.h"
+#include "machine.h"
+#include "gain_analysis.h"
+
+/* for each filter: */
+/* [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz */
+
+#ifdef WIN32
+#pragma warning ( disable : 4305 )
+#endif
+
+/*lint -save -e736 loss of precision */
+static const Float_t ABYule[9][2 * YULE_ORDER + 1] = {
+ {0.03857599435200, -3.84664617118067, -0.02160367184185, 7.81501653005538, -0.00123395316851,
+ -11.34170355132042, -0.00009291677959, 13.05504219327545, -0.01655260341619,
+ -12.28759895145294, 0.02161526843274, 9.48293806319790, -0.02074045215285, -5.87257861775999,
+ 0.00594298065125, 2.75465861874613, 0.00306428023191, -0.86984376593551, 0.00012025322027,
+ 0.13919314567432, 0.00288463683916},
+ {0.05418656406430, -3.47845948550071, -0.02911007808948, 6.36317777566148, -0.00848709379851,
+ -8.54751527471874, -0.00851165645469, 9.47693607801280, -0.00834990904936, -8.81498681370155,
+ 0.02245293253339, 6.85401540936998, -0.02596338512915, -4.39470996079559, 0.01624864962975,
+ 2.19611684890774, -0.00240879051584, -0.75104302451432, 0.00674613682247, 0.13149317958808,
+ -0.00187763777362},
+ {0.15457299681924, -2.37898834973084, -0.09331049056315, 2.84868151156327, -0.06247880153653,
+ -2.64577170229825, 0.02163541888798, 2.23697657451713, -0.05588393329856, -1.67148153367602,
+ 0.04781476674921, 1.00595954808547, 0.00222312597743, -0.45953458054983, 0.03174092540049,
+ 0.16378164858596, -0.01390589421898, -0.05032077717131, 0.00651420667831, 0.02347897407020,
+ -0.00881362733839},
+ {0.30296907319327, -1.61273165137247, -0.22613988682123, 1.07977492259970, -0.08587323730772,
+ -0.25656257754070, 0.03282930172664, -0.16276719120440, -0.00915702933434, -0.22638893773906,
+ -0.02364141202522, 0.39120800788284, -0.00584456039913, -0.22138138954925, 0.06276101321749,
+ 0.04500235387352, -0.00000828086748, 0.02005851806501, 0.00205861885564, 0.00302439095741,
+ -0.02950134983287},
+ {0.33642304856132, -1.49858979367799, -0.25572241425570, 0.87350271418188, -0.11828570177555,
+ 0.12205022308084, 0.11921148675203, -0.80774944671438, -0.07834489609479, 0.47854794562326,
+ -0.00469977914380, -0.12453458140019, -0.00589500224440, -0.04067510197014, 0.05724228140351,
+ 0.08333755284107, 0.00832043980773, -0.04237348025746, -0.01635381384540, 0.02977207319925,
+ -0.01760176568150},
+ {0.44915256608450, -0.62820619233671, -0.14351757464547, 0.29661783706366, -0.22784394429749,
+ -0.37256372942400, -0.01419140100551, 0.00213767857124, 0.04078262797139, -0.42029820170918,
+ -0.12398163381748, 0.22199650564824, 0.04097565135648, 0.00613424350682, 0.10478503600251,
+ 0.06747620744683, -0.01863887810927, 0.05784820375801, -0.03193428438915, 0.03222754072173,
+ 0.00541907748707},
+ {0.56619470757641, -1.04800335126349, -0.75464456939302, 0.29156311971249, 0.16242137742230,
+ -0.26806001042947, 0.16744243493672, 0.00819999645858, -0.18901604199609, 0.45054734505008,
+ 0.30931782841830, -0.33032403314006, -0.27562961986224, 0.06739368333110, 0.00647310677246,
+ -0.04784254229033, 0.08647503780351, 0.01639907836189, -0.03788984554840, 0.01807364323573,
+ -0.00588215443421},
+ {0.58100494960553, -0.51035327095184, -0.53174909058578, -0.31863563325245, -0.14289799034253,
+ -0.20256413484477, 0.17520704835522, 0.14728154134330, 0.02377945217615, 0.38952639978999,
+ 0.15558449135573, -0.23313271880868, -0.25344790059353, -0.05246019024463, 0.01628462406333,
+ -0.02505961724053, 0.06920467763959, 0.02442357316099, -0.03721611395801, 0.01818801111503,
+ -0.00749618797172},
+ {0.53648789255105, -0.25049871956020, -0.42163034350696, -0.43193942311114, -0.00275953611929,
+ -0.03424681017675, 0.04267842219415, -0.04678328784242, -0.10214864179676, 0.26408300200955,
+ 0.14590772289388, 0.15113130533216, -0.02459864859345, -0.17556493366449, -0.11202315195388,
+ -0.18823009262115, -0.04060034127000, 0.05477720428674, 0.04788665548180, 0.04704409688120,
+ -0.02217936801134}
+};
+
+static const Float_t ABButter[9][2 * BUTTER_ORDER + 1] = {
+ {0.98621192462708, -1.97223372919527, -1.97242384925416, 0.97261396931306, 0.98621192462708},
+ {0.98500175787242, -1.96977855582618, -1.97000351574484, 0.97022847566350, 0.98500175787242},
+ {0.97938932735214, -1.95835380975398, -1.95877865470428, 0.95920349965459, 0.97938932735214},
+ {0.97531843204928, -1.95002759149878, -1.95063686409857, 0.95124613669835, 0.97531843204928},
+ {0.97316523498161, -1.94561023566527, -1.94633046996323, 0.94705070426118, 0.97316523498161},
+ {0.96454515552826, -1.92783286977036, -1.92909031105652, 0.93034775234268, 0.96454515552826},
+ {0.96009142950541, -1.91858953033784, -1.92018285901082, 0.92177618768381, 0.96009142950541},
+ {0.95856916599601, -1.91542108074780, -1.91713833199203, 0.91885558323625, 0.95856916599601},
+ {0.94597685600279, -1.88903307939452, -1.89195371200558, 0.89487434461664, 0.94597685600279}
+};
+
+/*lint -restore */
+
+#ifdef WIN32
+#pragma warning ( default : 4305 )
+#endif
+
+/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */
+
+static void
+filterYule(const Float_t * input, Float_t * output, size_t nSamples, const Float_t * const kernel)
+{
+ /*register double y; */
+
+ while (nSamples--) {
+ *output = 1e-10 /* 1e-10 is a hack to avoid slowdown because of denormals */
+ + input[0] * kernel[0]
+ - output[-1] * kernel[1]
+ + input[-1] * kernel[2]
+ - output[-2] * kernel[3]
+ + input[-2] * kernel[4]
+ - output[-3] * kernel[5]
+ + input[-3] * kernel[6]
+ - output[-4] * kernel[7]
+ + input[-4] * kernel[8]
+ - output[-5] * kernel[9]
+ + input[-5] * kernel[10]
+ - output[-6] * kernel[11]
+ + input[-6] * kernel[12]
+ - output[-7] * kernel[13]
+ + input[-7] * kernel[14]
+ - output[-8] * kernel[15]
+ + input[-8] * kernel[16]
+ - output[-9] * kernel[17]
+ + input[-9] * kernel[18]
+ - output[-10] * kernel[19]
+ + input[-10] * kernel[20];
+ ++output;
+ ++input;
+ /* *output++ = (Float_t)y; */
+ }
+}
+
+static void
+filterButter(const Float_t * input, Float_t * output, size_t nSamples, const Float_t * const kernel)
+{ /*register double y; */
+
+ while (nSamples--) {
+ *output = input[0] * kernel[0]
+ - output[-1] * kernel[1]
+ + input[-1] * kernel[2]
+ - output[-2] * kernel[3]
+ + input[-2] * kernel[4];
+ ++output;
+ ++input;
+ /* *output++ = (Float_t)y; */
+ }
+}
+
+
+
+static int ResetSampleFrequency(replaygain_t * rgData, long samplefreq);
+
+/* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */
+
+int
+ResetSampleFrequency(replaygain_t * rgData, long samplefreq)
+{
+ int i;
+
+ /* zero out initial values */
+ for (i = 0; i < MAX_ORDER; i++)
+ rgData->linprebuf[i] = rgData->lstepbuf[i]
+ = rgData->loutbuf[i]
+ = rgData->rinprebuf[i]
+ = rgData->rstepbuf[i]
+ = rgData->routbuf[i] = 0.;
+
+ switch ((int) (samplefreq)) {
+ case 48000:
+ rgData->freqindex = 0;
+ break;
+ case 44100:
+ rgData->freqindex = 1;
+ break;
+ case 32000:
+ rgData->freqindex = 2;
+ break;
+ case 24000:
+ rgData->freqindex = 3;
+ break;
+ case 22050:
+ rgData->freqindex = 4;
+ break;
+ case 16000:
+ rgData->freqindex = 5;
+ break;
+ case 12000:
+ rgData->freqindex = 6;
+ break;
+ case 11025:
+ rgData->freqindex = 7;
+ break;
+ case 8000:
+ rgData->freqindex = 8;
+ break;
+ default:
+ return INIT_GAIN_ANALYSIS_ERROR;
+ }
+
+ rgData->sampleWindow =
+ (samplefreq * RMS_WINDOW_TIME_NUMERATOR + RMS_WINDOW_TIME_DENOMINATOR -
+ 1) / RMS_WINDOW_TIME_DENOMINATOR;
+
+ rgData->lsum = 0.;
+ rgData->rsum = 0.;
+ rgData->totsamp = 0;
+
+ memset(rgData->A, 0, sizeof(rgData->A));
+
+ return INIT_GAIN_ANALYSIS_OK;
+}
+
+int
+InitGainAnalysis(replaygain_t * rgData, long samplefreq)
+{
+ if (ResetSampleFrequency(rgData, samplefreq) != INIT_GAIN_ANALYSIS_OK) {
+ return INIT_GAIN_ANALYSIS_ERROR;
+ }
+
+ rgData->linpre = rgData->linprebuf + MAX_ORDER;
+ rgData->rinpre = rgData->rinprebuf + MAX_ORDER;
+ rgData->lstep = rgData->lstepbuf + MAX_ORDER;
+ rgData->rstep = rgData->rstepbuf + MAX_ORDER;
+ rgData->lout = rgData->loutbuf + MAX_ORDER;
+ rgData->rout = rgData->routbuf + MAX_ORDER;
+
+ memset(rgData->B, 0, sizeof(rgData->B));
+
+ return INIT_GAIN_ANALYSIS_OK;
+}
+
+/* returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not */
+
+static inline double
+fsqr(const double d)
+{
+ return d * d;
+}
+
+int
+AnalyzeSamples(replaygain_t * rgData, const Float_t * left_samples, const Float_t * right_samples,
+ size_t num_samples, int num_channels)
+{
+ const Float_t *curleft;
+ const Float_t *curright;
+ long batchsamples;
+ long cursamples;
+ long cursamplepos;
+ int i;
+
+ if (num_samples == 0)
+ return GAIN_ANALYSIS_OK;
+
+ cursamplepos = 0;
+ batchsamples = (long) num_samples;
+
+ switch (num_channels) {
+ case 1:
+ right_samples = left_samples;
+ break;
+ case 2:
+ break;
+ default:
+ return GAIN_ANALYSIS_ERROR;
+ }
+
+ if (num_samples < MAX_ORDER) {
+ memcpy(rgData->linprebuf + MAX_ORDER, left_samples, num_samples * sizeof(Float_t));
+ memcpy(rgData->rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t));
+ }
+ else {
+ memcpy(rgData->linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t));
+ memcpy(rgData->rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t));
+ }
+
+ while (batchsamples > 0) {
+ cursamples = batchsamples > rgData->sampleWindow - rgData->totsamp ?
+ rgData->sampleWindow - rgData->totsamp : batchsamples;
+ if (cursamplepos < MAX_ORDER) {
+ curleft = rgData->linpre + cursamplepos;
+ curright = rgData->rinpre + cursamplepos;
+ if (cursamples > MAX_ORDER - cursamplepos)
+ cursamples = MAX_ORDER - cursamplepos;
+ }
+ else {
+ curleft = left_samples + cursamplepos;
+ curright = right_samples + cursamplepos;
+ }
+
+ YULE_FILTER(curleft, rgData->lstep + rgData->totsamp, cursamples,
+ ABYule[rgData->freqindex]);
+ YULE_FILTER(curright, rgData->rstep + rgData->totsamp, cursamples,
+ ABYule[rgData->freqindex]);
+
+ BUTTER_FILTER(rgData->lstep + rgData->totsamp, rgData->lout + rgData->totsamp, cursamples,
+ ABButter[rgData->freqindex]);
+ BUTTER_FILTER(rgData->rstep + rgData->totsamp, rgData->rout + rgData->totsamp, cursamples,
+ ABButter[rgData->freqindex]);
+
+ curleft = rgData->lout + rgData->totsamp; /* Get the squared values */
+ curright = rgData->rout + rgData->totsamp;
+
+ i = cursamples % 8;
+ while (i--) {
+ rgData->lsum += fsqr(*curleft++);
+ rgData->rsum += fsqr(*curright++);
+ }
+ i = cursamples / 8;
+ while (i--) {
+ rgData->lsum += fsqr(curleft[0])
+ + fsqr(curleft[1])
+ + fsqr(curleft[2])
+ + fsqr(curleft[3])
+ + fsqr(curleft[4])
+ + fsqr(curleft[5])
+ + fsqr(curleft[6])
+ + fsqr(curleft[7]);
+ curleft += 8;
+ rgData->rsum += fsqr(curright[0])
+ + fsqr(curright[1])
+ + fsqr(curright[2])
+ + fsqr(curright[3])
+ + fsqr(curright[4])
+ + fsqr(curright[5])
+ + fsqr(curright[6])
+ + fsqr(curright[7]);
+ curright += 8;
+ }
+
+ batchsamples -= cursamples;
+ cursamplepos += cursamples;
+ rgData->totsamp += cursamples;
+ if (rgData->totsamp == rgData->sampleWindow) { /* Get the Root Mean Square (RMS) for this set of samples */
+ double const val =
+ STEPS_per_dB * 10. * log10((rgData->lsum + rgData->rsum) / rgData->totsamp * 0.5 +
+ 1.e-37);
+ size_t ival = (val <= 0) ? 0 : (size_t) val;
+ if (ival >= sizeof(rgData->A) / sizeof(*(rgData->A)))
+ ival = sizeof(rgData->A) / sizeof(*(rgData->A)) - 1;
+ rgData->A[ival]++;
+ rgData->lsum = rgData->rsum = 0.;
+ memmove(rgData->loutbuf, rgData->loutbuf + rgData->totsamp,
+ MAX_ORDER * sizeof(Float_t));
+ memmove(rgData->routbuf, rgData->routbuf + rgData->totsamp,
+ MAX_ORDER * sizeof(Float_t));
+ memmove(rgData->lstepbuf, rgData->lstepbuf + rgData->totsamp,
+ MAX_ORDER * sizeof(Float_t));
+ memmove(rgData->rstepbuf, rgData->rstepbuf + rgData->totsamp,
+ MAX_ORDER * sizeof(Float_t));
+ rgData->totsamp = 0;
+ }
+ if (rgData->totsamp > rgData->sampleWindow) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */
+ return GAIN_ANALYSIS_ERROR;
+ }
+ if (num_samples < MAX_ORDER) {
+ memmove(rgData->linprebuf, rgData->linprebuf + num_samples,
+ (MAX_ORDER - num_samples) * sizeof(Float_t));
+ memmove(rgData->rinprebuf, rgData->rinprebuf + num_samples,
+ (MAX_ORDER - num_samples) * sizeof(Float_t));
+ memcpy(rgData->linprebuf + MAX_ORDER - num_samples, left_samples,
+ num_samples * sizeof(Float_t));
+ memcpy(rgData->rinprebuf + MAX_ORDER - num_samples, right_samples,
+ num_samples * sizeof(Float_t));
+ }
+ else {
+ memcpy(rgData->linprebuf, left_samples + num_samples - MAX_ORDER,
+ MAX_ORDER * sizeof(Float_t));
+ memcpy(rgData->rinprebuf, right_samples + num_samples - MAX_ORDER,
+ MAX_ORDER * sizeof(Float_t));
+ }
+
+ return GAIN_ANALYSIS_OK;
+}
+
+
+static Float_t
+analyzeResult(uint32_t const *Array, size_t len)
+{
+ uint32_t elems;
+ uint32_t upper;
+ uint32_t sum;
+ size_t i;
+
+ elems = 0;
+ for (i = 0; i < len; i++)
+ elems += Array[i];
+ if (elems == 0)
+ return GAIN_NOT_ENOUGH_SAMPLES;
+
+ upper = (uint32_t) ceil(elems * (1. - RMS_PERCENTILE));
+ sum = 0;
+ for (i = len; i-- > 0;) {
+ sum += Array[i];
+ if (sum >= upper) {
+ break;
+ }
+ }
+
+ return (Float_t) ((Float_t) PINK_REF - (Float_t) i / (Float_t) STEPS_per_dB);
+}
+
+
+Float_t
+GetTitleGain(replaygain_t * rgData)
+{
+ Float_t retval;
+ unsigned int i;
+
+ retval = analyzeResult(rgData->A, sizeof(rgData->A) / sizeof(*(rgData->A)));
+
+ for (i = 0; i < sizeof(rgData->A) / sizeof(*(rgData->A)); i++) {
+ rgData->B[i] += rgData->A[i];
+ rgData->A[i] = 0;
+ }
+
+ for (i = 0; i < MAX_ORDER; i++)
+ rgData->linprebuf[i] = rgData->lstepbuf[i]
+ = rgData->loutbuf[i]
+ = rgData->rinprebuf[i]
+ = rgData->rstepbuf[i]
+ = rgData->routbuf[i] = 0.f;
+
+ rgData->totsamp = 0;
+ rgData->lsum = rgData->rsum = 0.;
+ return retval;
+}
+
+#if 0
+static Float_t GetAlbumGain(replaygain_t const* rgData);
+
+Float_t
+GetAlbumGain(replaygain_t const* rgData)
+{
+ return analyzeResult(rgData->B, sizeof(rgData->B) / sizeof(*(rgData->B)));
+}
+#endif
+
+/* end of gain_analysis.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/gain_analysis.h b/libnative/src/main/cpp/module/mp3/lame/gain_analysis.h
new file mode 100644
index 0000000000000000000000000000000000000000..a6b56ab3b5e66da5c051e92afee638e24fbf43d5
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/gain_analysis.h
@@ -0,0 +1,109 @@
+/*
+ * ReplayGainAnalysis - analyzes input samples and give the recommended dB change
+ * Copyright (C) 2001 David Robinson and Glen Sawyer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * concept and filter values by David Robinson (David@Robinson.org)
+ * -- blame him if you think the idea is flawed
+ * coding by Glen Sawyer (mp3gain@hotmail.com) 735 W 255 N, Orem, UT 84057-4505 USA
+ * -- blame him if you think this runs too slowly, or the coding is otherwise flawed
+ *
+ * For an explanation of the concepts and the basic algorithms involved, go to:
+ * http://www.replaygain.org/
+ */
+
+#ifndef GAIN_ANALYSIS_H
+#define GAIN_ANALYSIS_H
+
+#ifdef HAVE_INTTYPES_H
+# include
+#else
+# ifdef HAVE_STDINT_H
+# include
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ typedef sample_t Float_t; /* Type used for filtering */
+
+
+#define PINK_REF 64.82 /* 298640883795 */ /* calibration value for 89dB */
+
+
+#define YULE_ORDER 10
+#define BUTTER_ORDER 2
+#define YULE_FILTER filterYule
+#define BUTTER_FILTER filterButter
+#define RMS_PERCENTILE 0.95 /* percentile which is louder than the proposed level */
+#define MAX_SAMP_FREQ 48000L /* maximum allowed sample frequency [Hz] */
+#define RMS_WINDOW_TIME_NUMERATOR 1L
+#define RMS_WINDOW_TIME_DENOMINATOR 20L /* numerator / denominator = time slice size [s] */
+#define STEPS_per_dB 100 /* Table entries per dB */
+#define MAX_dB 120 /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */
+
+ enum { GAIN_NOT_ENOUGH_SAMPLES = -24601, GAIN_ANALYSIS_ERROR = 0, GAIN_ANALYSIS_OK =
+ 1, INIT_GAIN_ANALYSIS_ERROR = 0, INIT_GAIN_ANALYSIS_OK = 1
+ };
+
+ enum { MAX_ORDER = (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER)
+ , MAX_SAMPLES_PER_WINDOW = ((MAX_SAMP_FREQ * RMS_WINDOW_TIME_NUMERATOR) / RMS_WINDOW_TIME_DENOMINATOR + 1) /* max. Samples per Time slice */
+ };
+
+ struct replaygain_data {
+ Float_t linprebuf[MAX_ORDER * 2];
+ Float_t *linpre; /* left input samples, with pre-buffer */
+ Float_t lstepbuf[MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+ Float_t *lstep; /* left "first step" (i.e. post first filter) samples */
+ Float_t loutbuf[MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+ Float_t *lout; /* left "out" (i.e. post second filter) samples */
+ Float_t rinprebuf[MAX_ORDER * 2];
+ Float_t *rinpre; /* right input samples ... */
+ Float_t rstepbuf[MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+ Float_t *rstep;
+ Float_t routbuf[MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
+ Float_t *rout;
+ long sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */
+ long totsamp;
+ double lsum;
+ double rsum;
+ int freqindex;
+ int first;
+ uint32_t A[STEPS_per_dB * MAX_dB];
+ uint32_t B[STEPS_per_dB * MAX_dB];
+
+ };
+#ifndef replaygain_data_defined
+#define replaygain_data_defined
+ typedef struct replaygain_data replaygain_t;
+#endif
+
+
+
+
+ int InitGainAnalysis(replaygain_t * rgData, long samplefreq);
+ int AnalyzeSamples(replaygain_t * rgData, const Float_t * left_samples,
+ const Float_t * right_samples, size_t num_samples, int num_channels);
+ Float_t GetTitleGain(replaygain_t * rgData);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* GAIN_ANALYSIS_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/id3tag.c b/libnative/src/main/cpp/module/mp3/lame/id3tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8ea8f0ce44ad1ab098e704850381d6a42850a5b
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/id3tag.c
@@ -0,0 +1,1829 @@
+/*
+ * id3tag.c -- Write ID3 version 1 and 2 tags.
+ *
+ * Copyright (C) 2000 Don Melton
+ * Copyright (C) 2011-2012 Robert Hegemann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * HISTORY: This source file is part of LAME (see http://www.mp3dev.org)
+ * and was originally adapted by Conrad Sanderson
+ * from mp3info by Ricardo Cerqueira to write only ID3 version 1
+ * tags. Don Melton COMPLETELY rewrote it to support version
+ * 2 tags and be more conformant to other standards while remaining flexible.
+ *
+ * NOTE: See http://id3.org/ for more information about ID3 tag formats.
+ */
+
+/* $Id: id3tag.c,v 1.75.2.2 2012/01/08 23:49:58 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#ifdef STDC_HEADERS
+# include
+# include
+# include
+# include
+#else
+//# ifndef HAVE_STRCHR
+//# define strchr index
+//# define strrchr rindex
+//# endif
+char *strchr(), *strrchr();
+//# ifndef HAVE_MEMCPY
+//# define memcpy(d, s, n) bcopy ((s), (d), (n))
+//# endif
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "id3tag.h"
+#include "lame_global_flags.h"
+#include "util.h"
+#include "bitstream.h"
+
+#define lame_calloc(TYPE, COUNT) ((TYPE*)calloc(COUNT, sizeof(TYPE)))
+
+static const char *const genre_names[] = {
+ /*
+ * NOTE: The spelling of these genre names is identical to those found in
+ * Winamp and mp3info.
+ */
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
+ "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B",
+ "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
+ "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
+ "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
+ "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock",
+ "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
+ "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
+ "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
+ "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
+ "Native US", "Cabaret", "New Wave", "Psychedelic", "Rave",
+ "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
+ "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
+ "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin",
+ "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
+ "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
+ "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
+ "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
+ "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
+ "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
+ "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall",
+ "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
+ "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta",
+ "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
+ "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
+ "SynthPop"
+};
+
+#define GENRE_NAME_COUNT \
+ ((int)(sizeof genre_names / sizeof (const char *const)))
+
+static const int genre_alpha_map[] = {
+ 123, 34, 74, 73, 99, 20, 40, 26, 145, 90, 116, 41, 135, 85, 96, 138, 89, 0,
+ 107, 132, 65, 88, 104, 102, 97, 136, 61, 141, 32, 1, 112, 128, 57, 140, 2,
+ 139, 58, 3, 125, 50, 22, 4, 55, 127, 122, 120, 98, 52, 48, 54, 124, 25, 84,
+ 80, 115, 81, 119, 5, 30, 36, 59, 126, 38, 49, 91, 6, 129, 79, 137, 7, 35,
+ 100, 131, 19, 33, 46, 47, 8, 29, 146, 63, 86, 71, 45, 142, 9, 77, 82, 64,
+ 133, 10, 66, 39, 11, 103, 12, 75, 134, 13, 53, 62, 109, 117, 23, 108, 92,
+ 67, 93, 43, 121, 15, 68, 14, 16, 76, 87, 118, 17, 78, 143, 114, 110, 69, 21,
+ 111, 95, 105, 42, 37, 24, 56, 44, 101, 83, 94, 106, 147, 113, 18, 51, 130,
+ 144, 60, 70, 31, 72, 27, 28
+};
+
+#define GENRE_ALPHA_COUNT ((int)(sizeof genre_alpha_map / sizeof (int)))
+
+#define GENRE_INDEX_OTHER 12
+
+
+#define FRAME_ID(a, b, c, d) \
+ ( ((unsigned long)(a) << 24) \
+ | ((unsigned long)(b) << 16) \
+ | ((unsigned long)(c) << 8) \
+ | ((unsigned long)(d) << 0) )
+
+typedef enum UsualStringIDs { ID_TITLE = FRAME_ID('T', 'I', 'T', '2')
+ , ID_ARTIST = FRAME_ID('T', 'P', 'E', '1')
+ , ID_ALBUM = FRAME_ID('T', 'A', 'L', 'B')
+ , ID_GENRE = FRAME_ID('T', 'C', 'O', 'N')
+ , ID_ENCODER = FRAME_ID('T', 'S', 'S', 'E')
+ , ID_PLAYLENGTH = FRAME_ID('T', 'L', 'E', 'N')
+ , ID_COMMENT = FRAME_ID('C', 'O', 'M', 'M') /* full text string */
+} UsualStringIDs;
+
+typedef enum NumericStringIDs { ID_DATE = FRAME_ID('T', 'D', 'A', 'T') /* "ddMM" */
+ , ID_TIME = FRAME_ID('T', 'I', 'M', 'E') /* "hhmm" */
+ , ID_TPOS = FRAME_ID('T', 'P', 'O', 'S') /* '0'-'9' and '/' allowed */
+ , ID_TRACK = FRAME_ID('T', 'R', 'C', 'K') /* '0'-'9' and '/' allowed */
+ , ID_YEAR = FRAME_ID('T', 'Y', 'E', 'R') /* "yyyy" */
+} NumericStringIDs;
+
+typedef enum MiscIDs { ID_TXXX = FRAME_ID('T', 'X', 'X', 'X')
+ , ID_WXXX = FRAME_ID('W', 'X', 'X', 'X')
+ , ID_SYLT = FRAME_ID('S', 'Y', 'L', 'T')
+ , ID_APIC = FRAME_ID('A', 'P', 'I', 'C')
+ , ID_GEOB = FRAME_ID('G', 'E', 'O', 'B')
+ , ID_PCNT = FRAME_ID('P', 'C', 'N', 'T')
+ , ID_AENC = FRAME_ID('A', 'E', 'N', 'C')
+ , ID_LINK = FRAME_ID('L', 'I', 'N', 'K')
+ , ID_ENCR = FRAME_ID('E', 'N', 'C', 'R')
+ , ID_GRID = FRAME_ID('G', 'R', 'I', 'D')
+ , ID_PRIV = FRAME_ID('P', 'R', 'I', 'V')
+ , ID_VSLT = FRAME_ID('V', 'S', 'L', 'T') /* full text string */
+ , ID_USER = FRAME_ID('U', 'S', 'E', 'R') /* full text string */
+ , ID_PCST = FRAME_ID('P', 'C', 'S', 'T') /* iTunes Podcast indicator, only presence important */
+ , ID_WFED = FRAME_ID('W', 'F', 'E', 'D') /* iTunes Podcast URL as TEXT FRAME !!! violates standard */
+} MiscIDs;
+
+
+static int
+frame_id_matches(int id, int mask)
+{
+ int result = 0, i, window = 0xff;
+ for (i = 0; i < 4; ++i, window <<= 8) {
+ int const mw = (mask & window);
+ int const iw = (id & window);
+ if (mw != 0 && mw != iw) {
+ result |= iw;
+ }
+ }
+ return result;
+}
+
+static int
+isFrameIdMatching(int id, int mask)
+{
+ return frame_id_matches(id, mask) == 0 ? 1 : 0;
+}
+
+static int
+test_tag_spec_flags(lame_internal_flags const *gfc, unsigned int tst)
+{
+ return (gfc->tag_spec.flags & tst) != 0u ? 1 : 0;
+}
+
+#if 0
+static void
+debug_tag_spec_flags(lame_internal_flags * gfc, const char* info)
+{
+ MSGF(gfc, "%s\n", info);
+ MSGF(gfc, "CHANGED_FLAG : %d\n", test_tag_spec_flags(gfc, CHANGED_FLAG ));
+ MSGF(gfc, "ADD_V2_FLAG : %d\n", test_tag_spec_flags(gfc, ADD_V2_FLAG ));
+ MSGF(gfc, "V1_ONLY_FLAG : %d\n", test_tag_spec_flags(gfc, V1_ONLY_FLAG ));
+ MSGF(gfc, "V2_ONLY_FLAG : %d\n", test_tag_spec_flags(gfc, V2_ONLY_FLAG ));
+ MSGF(gfc, "SPACE_V1_FLAG : %d\n", test_tag_spec_flags(gfc, SPACE_V1_FLAG));
+ MSGF(gfc, "PAD_V2_FLAG : %d\n", test_tag_spec_flags(gfc, PAD_V2_FLAG ));
+}
+#endif
+
+
+
+static int
+id3v2_add_ucs2(lame_t gfp, uint32_t frame_id, char const *lang, unsigned short const *desc, unsigned short const *text);
+static int
+id3v2_add_latin1(lame_t gfp, uint32_t frame_id, char const *lang, char const *desc, char const *text);
+
+static void
+copyV1ToV2(lame_t gfp, int frame_id, char const *s)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc != 0) {
+ unsigned int flags = gfc->tag_spec.flags;
+ id3v2_add_latin1(gfp, frame_id, "XXX", 0, s);
+ gfc->tag_spec.flags = flags;
+#if 0
+ debug_tag_spec_flags(gfc, "copyV1ToV2");
+#endif
+ }
+}
+
+
+static void
+id3v2AddLameVersion(lame_t gfp)
+{
+ char buffer[1024];
+ const char *b = get_lame_os_bitness();
+ const char *v = get_lame_version();
+ const char *u = get_lame_url();
+ const size_t lenb = strlen(b);
+
+ if (lenb > 0) {
+ sprintf(buffer, "LAME %s version %s (%s)", b, v, u);
+ }
+ else {
+ sprintf(buffer, "LAME version %s (%s)", v, u);
+ }
+ copyV1ToV2(gfp, ID_ENCODER, buffer);
+}
+
+static void
+id3v2AddAudioDuration(lame_t gfp, double ms)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ char buffer[1024];
+ double const max_ulong = MAX_U_32_NUM;
+ unsigned long playlength_ms;
+
+ ms *= 1000;
+ ms /= cfg->samplerate_in;
+ if (ms > max_ulong) {
+ playlength_ms = max_ulong;
+ }
+ else if (ms < 0) {
+ playlength_ms = 0;
+ }
+ else {
+ playlength_ms = ms;
+ }
+ sprintf(buffer, "%lu", playlength_ms);
+ copyV1ToV2(gfp, ID_PLAYLENGTH, buffer);
+}
+
+void
+id3tag_genre_list(void (*handler) (int, const char *, void *), void *cookie)
+{
+ if (handler) {
+ int i;
+ for (i = 0; i < GENRE_NAME_COUNT; ++i) {
+ if (i < GENRE_ALPHA_COUNT) {
+ int j = genre_alpha_map[i];
+ handler(j, genre_names[j], cookie);
+ }
+ }
+ }
+}
+
+#define GENRE_NUM_UNKNOWN 255
+
+
+
+void
+id3tag_init(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ free_id3tag(gfc);
+ memset(&gfc->tag_spec, 0, sizeof gfc->tag_spec);
+ gfc->tag_spec.genre_id3v1 = GENRE_NUM_UNKNOWN;
+ gfc->tag_spec.padding_size = 128;
+ id3v2AddLameVersion(gfp);
+}
+
+
+
+void
+id3tag_add_v2(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
+ gfc->tag_spec.flags |= ADD_V2_FLAG;
+}
+
+void
+id3tag_v1_only(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ gfc->tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
+ gfc->tag_spec.flags |= V1_ONLY_FLAG;
+}
+
+void
+id3tag_v2_only(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
+ gfc->tag_spec.flags |= V2_ONLY_FLAG;
+}
+
+void
+id3tag_space_v1(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ gfc->tag_spec.flags &= ~V2_ONLY_FLAG;
+ gfc->tag_spec.flags |= SPACE_V1_FLAG;
+}
+
+void
+id3tag_pad_v2(lame_t gfp)
+{
+ id3tag_set_pad(gfp, 128);
+}
+
+void
+id3tag_set_pad(lame_t gfp, size_t n)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ gfc->tag_spec.flags &= ~V1_ONLY_FLAG;
+ gfc->tag_spec.flags |= PAD_V2_FLAG;
+ gfc->tag_spec.flags |= ADD_V2_FLAG;
+ gfc->tag_spec.padding_size = (unsigned int)n;
+}
+
+static int
+hasUcs2ByteOrderMarker(unsigned short bom)
+{
+ if (bom == 0xFFFEu || bom == 0xFEFFu) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static unsigned short
+swap_bytes(unsigned short w)
+{
+ return (0xff00u & (w << 8)) | (0x00ffu & (w >> 8));
+}
+
+
+static unsigned short
+toLittleEndian(unsigned short bom, unsigned short c)
+{
+ if (bom == 0xFFFEu) {
+ return swap_bytes(c);
+ }
+ return c;
+}
+
+static unsigned short
+fromLatin1Char(const unsigned short* s, unsigned short c)
+{
+ if (s[0] == 0xFFFEu) {
+ return swap_bytes(c);
+ }
+ return c;
+}
+
+
+static size_t
+local_strdup(char **dst, const char *src)
+{
+ if (dst == 0) {
+ return 0;
+ }
+ free(*dst);
+ *dst = 0;
+ if (src != 0) {
+ size_t n;
+ for (n = 0; src[n] != 0; ++n) { /* calc src string length */
+ }
+ if (n > 0) { /* string length without zero termination */
+ assert(sizeof(*src) == sizeof(**dst));
+ *dst = lame_calloc(char, n + 1);
+ if (*dst != 0) {
+ memcpy(*dst, src, n * sizeof(**dst));
+ (*dst)[n] = 0;
+ return n;
+ }
+ }
+ }
+ return 0;
+}
+
+static size_t
+local_ucs2_strdup(unsigned short **dst, unsigned short const *src)
+{
+ if (dst == 0) {
+ return 0;
+ }
+ free(*dst); /* free old string pointer */
+ *dst = 0;
+ if (src != 0) {
+ size_t n;
+ for (n = 0; src[n] != 0; ++n) { /* calc src string length */
+ }
+ if (n > 0) { /* string length without zero termination */
+ assert(sizeof(*src) >= 2);
+ assert(sizeof(*src) == sizeof(**dst));
+ *dst = lame_calloc(unsigned short, n + 1);
+ if (*dst != 0) {
+ memcpy(*dst, src, n * sizeof(**dst));
+ (*dst)[n] = 0;
+ return n;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static size_t
+local_ucs2_strlen(unsigned short const *s)
+{
+ size_t n = 0;
+ if (s != 0) {
+ while (*s++) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+
+static size_t
+local_ucs2_substr(unsigned short** dst, unsigned short const* src, size_t start, size_t end)
+{
+ size_t const len = 1 + 1 + ((start < end) ? (end - start) : 0);
+ size_t n = 0;
+ unsigned short *ptr = lame_calloc(unsigned short, len);
+ *dst = ptr;
+ if (ptr == 0 || src == 0) {
+ return 0;
+ }
+ if (hasUcs2ByteOrderMarker(src[0])) {
+ ptr[n++] = src[0];
+ if (start == 0) {
+ ++start;
+ }
+ }
+ while (start < end) {
+ ptr[n++] = src[start++];
+ }
+ ptr[n] = 0;
+ return n;
+}
+
+static int
+local_ucs2_pos(unsigned short const* str, unsigned short c)
+{
+ int i;
+ for (i = 0; str != 0 && str[i] != 0; ++i) {
+ if (str[i] == c) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+local_char_pos(char const* str, char c)
+{
+ int i;
+ for (i = 0; str != 0 && str[i] != 0; ++i) {
+ if (str[i] == c) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+maybeLatin1(unsigned short const* text)
+{
+ if (text) {
+ unsigned short bom = *text++;
+ while (*text) {
+ unsigned short c = toLittleEndian(bom, *text++);
+ if (c > 0x00fe) return 0;
+ }
+ }
+ return 1;
+}
+
+static int searchGenre(char const* genre);
+static int sloppySearchGenre(char const* genre);
+
+static int
+lookupGenre(char const* genre)
+{
+ char *str;
+ int num = strtol(genre, &str, 10);
+ /* is the input a string or a valid number? */
+ if (*str) {
+ num = searchGenre(genre);
+ if (num == GENRE_NAME_COUNT) {
+ num = sloppySearchGenre(genre);
+ }
+ if (num == GENRE_NAME_COUNT) {
+ return -2; /* no common genre text found */
+ }
+ }
+ else {
+ if ((num < 0) || (num >= GENRE_NAME_COUNT)) {
+ return -1; /* number unknown */
+ }
+ }
+ return num;
+}
+
+static unsigned char *
+writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n);
+
+static char*
+local_strdup_utf16_to_latin1(unsigned short const* utf16)
+{
+ size_t len = local_ucs2_strlen(utf16);
+ unsigned char* latin1 = lame_calloc(unsigned char, len+1);
+ writeLoBytes(latin1, utf16, len);
+ return (char*)latin1;
+}
+
+
+static int
+id3tag_set_genre_utf16(lame_t gfp, unsigned short const* text)
+{
+ lame_internal_flags* gfc = gfp->internal_flags;
+ int ret;
+ if (text == 0) {
+ return -3;
+ }
+ if (!hasUcs2ByteOrderMarker(text[0])) {
+ return -3;
+ }
+ if (maybeLatin1(text)) {
+ char* latin1 = local_strdup_utf16_to_latin1(text);
+ int num = lookupGenre(latin1);
+ free(latin1);
+ if (num == -1) return -1; /* number out of range */
+ if (num >= 0) { /* common genre found */
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ gfc->tag_spec.genre_id3v1 = num;
+ copyV1ToV2(gfp, ID_GENRE, genre_names[num]);
+ return 0;
+ }
+ }
+ ret = id3v2_add_ucs2(gfp, ID_GENRE, 0, 0, text);
+ if (ret == 0) {
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
+ }
+ return ret;
+}
+
+/*
+Some existing options for ID3 tag can be specified by --tv option
+as follows.
+--tt , --tv TIT2=value
+--ta , --tv TPE1=value
+--tl , --tv TALB=value
+--ty , --tv TYER=value
+--tn , --tv TRCK=value
+--tg , --tv TCON=value
+(although some are not exactly same)*/
+
+int
+id3tag_set_albumart(lame_t gfp, const char *image, size_t size)
+{
+ int mimetype = 0;
+ unsigned char const *data = (unsigned char const *) image;
+ lame_internal_flags *gfc = gfp->internal_flags;
+
+ /* determine MIME type from the actual image data */
+ if (2 < size && data[0] == 0xFF && data[1] == 0xD8) {
+ mimetype = MIMETYPE_JPEG;
+ }
+ else if (4 < size && data[0] == 0x89 && strncmp((const char *) &data[1], "PNG", 3) == 0) {
+ mimetype = MIMETYPE_PNG;
+ }
+ else if (4 < size && strncmp((const char *) data, "GIF8", 4) == 0) {
+ mimetype = MIMETYPE_GIF;
+ }
+ else {
+ return -1;
+ }
+ if (gfc->tag_spec.albumart != 0) {
+ free(gfc->tag_spec.albumart);
+ gfc->tag_spec.albumart = 0;
+ gfc->tag_spec.albumart_size = 0;
+ gfc->tag_spec.albumart_mimetype = MIMETYPE_NONE;
+ }
+ if (size < 1) {
+ return 0;
+ }
+ gfc->tag_spec.albumart = lame_calloc(unsigned char, size);
+ if (gfc->tag_spec.albumart != 0) {
+ memcpy(gfc->tag_spec.albumart, image, size);
+ gfc->tag_spec.albumart_size = (unsigned int)size;
+ gfc->tag_spec.albumart_mimetype = mimetype;
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ id3tag_add_v2(gfp);
+ }
+ return 0;
+}
+
+static unsigned char *
+set_4_byte_value(unsigned char *bytes, uint32_t value)
+{
+ int i;
+ for (i = 3; i >= 0; --i) {
+ bytes[i] = value & 0xffUL;
+ value >>= 8;
+ }
+ return bytes + 4;
+}
+
+static uint32_t
+toID3v2TagId(char const *s)
+{
+ unsigned int i, x = 0;
+ if (s == 0) {
+ return 0;
+ }
+ for (i = 0; i < 4 && s[i] != 0; ++i) {
+ char const c = s[i];
+ unsigned int const u = 0x0ff & c;
+ x <<= 8;
+ x |= u;
+ if (c < 'A' || 'Z' < c) {
+ if (c < '0' || '9' < c) {
+ return 0;
+ }
+ }
+ }
+ return x;
+}
+
+static uint32_t
+toID3v2TagId_ucs2(unsigned short const *s)
+{
+ unsigned int i, x = 0;
+ unsigned short bom = 0;
+ if (s == 0) {
+ return 0;
+ }
+ bom = s[0];
+ if (hasUcs2ByteOrderMarker(bom)) {
+ ++s;
+ }
+ for (i = 0; i < 4 && s[i] != 0; ++i) {
+ unsigned short const c = toLittleEndian(bom, s[i]);
+ if (c < 'A' || 'Z' < c) {
+ if (c < '0' || '9' < c) {
+ return 0;
+ }
+ }
+ x <<= 8;
+ x |= c;
+ }
+ return x;
+}
+
+#if 0
+static int
+isNumericString(uint32_t frame_id)
+{
+ switch (frame_id) {
+ case ID_DATE:
+ case ID_TIME:
+ case ID_TPOS:
+ case ID_TRACK:
+ case ID_YEAR:
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static int
+isMultiFrame(uint32_t frame_id)
+{
+ switch (frame_id) {
+ case ID_TXXX:
+ case ID_WXXX:
+ case ID_COMMENT:
+ case ID_SYLT:
+ case ID_APIC:
+ case ID_GEOB:
+ case ID_PCNT:
+ case ID_AENC:
+ case ID_LINK:
+ case ID_ENCR:
+ case ID_GRID:
+ case ID_PRIV:
+ return 1;
+ }
+ return 0;
+}
+
+#if 0
+static int
+isFullTextString(int frame_id)
+{
+ switch (frame_id) {
+ case ID_VSLT:
+ case ID_COMMENT:
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static FrameDataNode *
+findNode(id3tag_spec const *tag, uint32_t frame_id, FrameDataNode const *last)
+{
+ FrameDataNode *node = last ? last->nxt : tag->v2_head;
+ while (node != 0) {
+ if (node->fid == frame_id) {
+ return node;
+ }
+ node = node->nxt;
+ }
+ return 0;
+}
+
+static void
+appendNode(id3tag_spec * tag, FrameDataNode * node)
+{
+ if (tag->v2_tail == 0 || tag->v2_head == 0) {
+ tag->v2_head = node;
+ tag->v2_tail = node;
+ }
+ else {
+ tag->v2_tail->nxt = node;
+ tag->v2_tail = node;
+ }
+}
+
+static void
+setLang(char *dst, char const *src)
+{
+ int i;
+ if (src == 0 || src[0] == 0) {
+ dst[0] = 'X';
+ dst[1] = 'X';
+ dst[2] = 'X';
+ }
+ else {
+ for (i = 0; i < 3 && src && *src; ++i) {
+ dst[i] = src[i];
+ }
+ for (; i < 3; ++i) {
+ dst[i] = ' ';
+ }
+ }
+}
+
+static int
+isSameLang(char const *l1, char const *l2)
+{
+ char d[3];
+ int i;
+ setLang(d, l2);
+ for (i = 0; i < 3; ++i) {
+ char a = tolower(l1[i]);
+ char b = tolower(d[i]);
+ if (a < ' ')
+ a = ' ';
+ if (b < ' ')
+ b = ' ';
+ if (a != b) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+isSameDescriptor(FrameDataNode const *node, char const *dsc)
+{
+ size_t i;
+ if (node->dsc.enc == 1 && node->dsc.dim > 0) {
+ return 0;
+ }
+ for (i = 0; i < node->dsc.dim; ++i) {
+ if (!dsc || node->dsc.ptr.l[i] != dsc[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+isSameDescriptorUcs2(FrameDataNode const *node, unsigned short const *dsc)
+{
+ size_t i;
+ if (node->dsc.enc != 1 && node->dsc.dim > 0) {
+ return 0;
+ }
+ for (i = 0; i < node->dsc.dim; ++i) {
+ if (!dsc || node->dsc.ptr.u[i] != dsc[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+id3v2_add_ucs2(lame_t gfp, uint32_t frame_id, char const *lang, unsigned short const *desc, unsigned short const *text)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc != 0) {
+ FrameDataNode *node = findNode(&gfc->tag_spec, frame_id, 0);
+ if (isMultiFrame(frame_id)) {
+ while (node) {
+ if (isSameLang(node->lng, lang)) {
+ if (isSameDescriptorUcs2(node, desc)) {
+ break;
+ }
+ }
+ node = findNode(&gfc->tag_spec, frame_id, node);
+ }
+ }
+ if (node == 0) {
+ node = lame_calloc(FrameDataNode, 1);
+ if (node == 0) {
+ return -254; /* memory problem */
+ }
+ appendNode(&gfc->tag_spec, node);
+ }
+ node->fid = frame_id;
+ setLang(node->lng, lang);
+ node->dsc.dim = local_ucs2_strdup(&node->dsc.ptr.u, desc);
+ node->dsc.enc = 1;
+ node->txt.dim = local_ucs2_strdup(&node->txt.ptr.u, text);
+ node->txt.enc = 1;
+ gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
+ return 0;
+ }
+ return -255;
+}
+
+static int
+id3v2_add_latin1(lame_t gfp, uint32_t frame_id, char const *lang, char const *desc, char const *text)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc != 0) {
+ FrameDataNode *node = findNode(&gfc->tag_spec, frame_id, 0);
+ if (isMultiFrame(frame_id)) {
+ while (node) {
+ if (isSameLang(node->lng, lang)) {
+ if (isSameDescriptor(node, desc)) {
+ break;
+ }
+ }
+ node = findNode(&gfc->tag_spec, frame_id, node);
+ }
+ }
+ if (node == 0) {
+ node = lame_calloc(FrameDataNode, 1);
+ if (node == 0) {
+ return -254; /* memory problem */
+ }
+ appendNode(&gfc->tag_spec, node);
+ }
+ node->fid = frame_id;
+ setLang(node->lng, lang);
+ node->dsc.dim = local_strdup(&node->dsc.ptr.l, desc);
+ node->dsc.enc = 0;
+ node->txt.dim = local_strdup(&node->txt.ptr.l, text);
+ node->txt.enc = 0;
+ gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
+ return 0;
+ }
+ return -255;
+}
+
+
+static int
+id3tag_set_userinfo_latin1(lame_t gfp, uint32_t id, char const *fieldvalue)
+{
+ char const separator = '=';
+ int rc = -7;
+ int a = local_char_pos(fieldvalue, separator);
+ if (a >= 0) {
+ char* dup = 0;
+ local_strdup(&dup, fieldvalue);
+ dup[a] = 0;
+ rc = id3v2_add_latin1(gfp, id, "XXX", dup, dup+a+1);
+ free(dup);
+ }
+ return rc;
+}
+
+static int
+id3tag_set_userinfo_ucs2(lame_t gfp, uint32_t id, unsigned short const *fieldvalue)
+{
+ unsigned short const separator = fromLatin1Char(fieldvalue,'=');
+ int rc = -7;
+ size_t b = local_ucs2_strlen(fieldvalue);
+ int a = local_ucs2_pos(fieldvalue, separator);
+ if (a >= 0) {
+ unsigned short* dsc = 0, *val = 0;
+ local_ucs2_substr(&dsc, fieldvalue, 0, a);
+ local_ucs2_substr(&val, fieldvalue, a+1, b);
+ rc = id3v2_add_ucs2(gfp, id, "XXX", dsc, val);
+ free(dsc);
+ free(val);
+ }
+ return rc;
+}
+
+int
+id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text)
+{
+ uint32_t const frame_id = toID3v2TagId(id);
+ if (frame_id == 0) {
+ return -1;
+ }
+ if (text == 0) {
+ return 0;
+ }
+ if (!hasUcs2ByteOrderMarker(text[0])) {
+ return -3; /* BOM missing */
+ }
+ if (frame_id == ID_TXXX || frame_id == ID_WXXX || frame_id == ID_COMMENT) {
+ return id3tag_set_userinfo_ucs2(gfp, frame_id, text);
+ }
+ if (frame_id == ID_GENRE) {
+ return id3tag_set_genre_utf16(gfp, text);
+ }
+ if (frame_id == ID_PCST) {
+ return id3v2_add_ucs2(gfp, frame_id, 0, 0, text);
+ }
+ if (frame_id == ID_USER) {
+ return id3v2_add_ucs2(gfp, frame_id, "XXX", text, 0);
+ }
+ if (frame_id == ID_WFED) {
+ return id3v2_add_ucs2(gfp, frame_id, 0, text, 0); /* iTunes expects WFED to be a text frame */
+ }
+ if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
+ ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
+#if 0
+ if (isNumericString(frame_id)) {
+ return -2; /* must be Latin-1 encoded */
+ }
+#endif
+ return id3v2_add_ucs2(gfp, frame_id, 0, 0, text);
+ }
+ return -255; /* not supported by now */
+}
+
+extern int
+id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text);
+
+int
+id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text)
+{
+ return id3tag_set_textinfo_utf16(gfp, id, text);
+}
+
+int
+id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text)
+{
+ uint32_t const frame_id = toID3v2TagId(id);
+ if (frame_id == 0) {
+ return -1;
+ }
+ if (text == 0) {
+ return 0;
+ }
+ if (frame_id == ID_TXXX || frame_id == ID_WXXX || frame_id == ID_COMMENT) {
+ return id3tag_set_userinfo_latin1(gfp, frame_id, text);
+ }
+ if (frame_id == ID_GENRE) {
+ return id3tag_set_genre(gfp, text);
+ }
+ if (frame_id == ID_PCST) {
+ return id3v2_add_latin1(gfp, frame_id, 0, 0, text);
+ }
+ if (frame_id == ID_USER) {
+ return id3v2_add_latin1(gfp, frame_id, "XXX", text, 0);
+ }
+ if (frame_id == ID_WFED) {
+ return id3v2_add_latin1(gfp, frame_id, 0, text, 0); /* iTunes expects WFED to be a text frame */
+ }
+ if (isFrameIdMatching(frame_id, FRAME_ID('T', 0, 0, 0))
+ ||isFrameIdMatching(frame_id, FRAME_ID('W', 0, 0, 0))) {
+ return id3v2_add_latin1(gfp, frame_id, 0, 0, text);
+ }
+ return -255; /* not supported by now */
+}
+
+
+int
+id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text)
+{
+ return id3v2_add_latin1(gfp, ID_COMMENT, lang, desc, text);
+}
+
+
+int
+id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text)
+{
+ return id3v2_add_ucs2(gfp, ID_COMMENT, lang, desc, text);
+}
+
+extern int
+id3tag_set_comment_ucs2(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text);
+
+
+int
+id3tag_set_comment_ucs2(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text)
+{
+ return id3tag_set_comment_utf16(gfp, lang, desc, text);
+}
+
+
+void
+id3tag_set_title(lame_t gfp, const char *title)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc && title && *title) {
+ local_strdup(&gfc->tag_spec.title, title);
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ copyV1ToV2(gfp, ID_TITLE, title);
+ }
+}
+
+void
+id3tag_set_artist(lame_t gfp, const char *artist)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc && artist && *artist) {
+ local_strdup(&gfc->tag_spec.artist, artist);
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ copyV1ToV2(gfp, ID_ARTIST, artist);
+ }
+}
+
+void
+id3tag_set_album(lame_t gfp, const char *album)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc && album && *album) {
+ local_strdup(&gfc->tag_spec.album, album);
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ copyV1ToV2(gfp, ID_ALBUM, album);
+ }
+}
+
+void
+id3tag_set_year(lame_t gfp, const char *year)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc && year && *year) {
+ int num = atoi(year);
+ if (num < 0) {
+ num = 0;
+ }
+ /* limit a year to 4 digits so it fits in a version 1 tag */
+ if (num > 9999) {
+ num = 9999;
+ }
+ if (num) {
+ gfc->tag_spec.year = num;
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ }
+ copyV1ToV2(gfp, ID_YEAR, year);
+ }
+}
+
+void
+id3tag_set_comment(lame_t gfp, const char *comment)
+{
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ if (gfc && comment && *comment) {
+ local_strdup(&gfc->tag_spec.comment, comment);
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ {
+ uint32_t const flags = gfc->tag_spec.flags;
+ id3v2_add_latin1(gfp, ID_COMMENT, "XXX", "", comment);
+ gfc->tag_spec.flags = flags;
+ }
+ }
+}
+
+int
+id3tag_set_track(lame_t gfp, const char *track)
+{
+ char const *trackcount;
+ lame_internal_flags *gfc = gfp != 0 ? gfp->internal_flags : 0;
+ int ret = 0;
+
+ if (gfc && track && *track) {
+ int num = atoi(track);
+ /* check for valid ID3v1 track number range */
+ if (num < 1 || num > 255) {
+ num = 0;
+ ret = -1; /* track number out of ID3v1 range, ignored for ID3v1 */
+ gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
+ }
+ if (num) {
+ gfc->tag_spec.track_id3v1 = num;
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ }
+ /* Look for the total track count after a "/", same restrictions */
+ trackcount = strchr(track, '/');
+ if (trackcount && *trackcount) {
+ gfc->tag_spec.flags |= (CHANGED_FLAG | ADD_V2_FLAG);
+ }
+ copyV1ToV2(gfp, ID_TRACK, track);
+ }
+ return ret;
+}
+
+/* would use real "strcasecmp" but it isn't portable */
+static int
+local_strcasecmp(const char *s1, const char *s2)
+{
+ unsigned char c1;
+ unsigned char c2;
+ do {
+ c1 = tolower(*s1);
+ c2 = tolower(*s2);
+ if (!c1) {
+ break;
+ }
+ ++s1;
+ ++s2;
+ } while (c1 == c2);
+ return c1 - c2;
+}
+
+
+static
+const char* nextUpperAlpha(const char* p, char x)
+{
+ char c;
+ for(c = toupper(*p); *p != 0; c = toupper(*++p)) {
+ if ('A' <= c && c <= 'Z') {
+ if (c != x) {
+ return p;
+ }
+ }
+ }
+ return p;
+}
+
+
+static int
+sloppyCompared(const char* p, const char* q)
+{
+ char cp, cq;
+ p = nextUpperAlpha(p, 0);
+ q = nextUpperAlpha(q, 0);
+ cp = toupper(*p);
+ cq = toupper(*q);
+ while (cp == cq) {
+ if (cp == 0) {
+ return 1;
+ }
+ if (p[1] == '.') { /* some abbrevation */
+ while (*q && *q++ != ' ') {
+ }
+ }
+ p = nextUpperAlpha(p, cp);
+ q = nextUpperAlpha(q, cq);
+ cp = toupper(*p);
+ cq = toupper(*q);
+ }
+ return 0;
+}
+
+
+static int
+sloppySearchGenre(const char *genre)
+{
+ int i;
+ for (i = 0; i < GENRE_NAME_COUNT; ++i) {
+ if (sloppyCompared(genre, genre_names[i])) {
+ return i;
+ }
+ }
+ return GENRE_NAME_COUNT;
+}
+
+
+static int
+searchGenre(const char* genre)
+{
+ int i;
+ for (i = 0; i < GENRE_NAME_COUNT; ++i) {
+ if (!local_strcasecmp(genre, genre_names[i])) {
+ return i;
+ }
+ }
+ return GENRE_NAME_COUNT;
+}
+
+
+int
+id3tag_set_genre(lame_t gfp, const char *genre)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+ int ret = 0;
+ if (genre && *genre) {
+ int const num = lookupGenre(genre);
+ if (num == -1) return num;
+ gfc->tag_spec.flags |= CHANGED_FLAG;
+ if (num >= 0) {
+ gfc->tag_spec.genre_id3v1 = num;
+ genre = genre_names[num];
+ }
+ else {
+ gfc->tag_spec.genre_id3v1 = GENRE_INDEX_OTHER;
+ gfc->tag_spec.flags |= ADD_V2_FLAG;
+ }
+ copyV1ToV2(gfp, ID_GENRE, genre);
+ }
+ return ret;
+}
+
+
+static size_t
+sizeOfNode(FrameDataNode const *node)
+{
+ size_t n = 0;
+ if (node) {
+ n = 10; /* header size */
+ n += 1; /* text encoding flag */
+ switch (node->txt.enc) {
+ default:
+ case 0:
+ if (node->dsc.dim > 0) {
+ n += node->dsc.dim + 1;
+ }
+ n += node->txt.dim;
+ break;
+ case 1:
+ if (node->dsc.dim > 0) {
+ n += (node->dsc.dim+1) * 2;
+ }
+ n += node->txt.dim * 2;
+ break;
+ }
+ }
+ return n;
+}
+
+static size_t
+sizeOfCommentNode(FrameDataNode const *node)
+{
+ size_t n = 0;
+ if (node) {
+ n = 10; /* header size */
+ n += 1; /* text encoding flag */
+ n += 3; /* language */
+ switch (node->dsc.enc) {
+ default:
+ case 0:
+ n += 1 + node->dsc.dim;
+ break;
+ case 1:
+ n += 2 + node->dsc.dim * 2;
+ break;
+ }
+ switch (node->txt.enc) {
+ default:
+ case 0:
+ n += node->txt.dim;
+ break;
+ case 1:
+ n += node->txt.dim * 2;
+ break;
+ }
+ }
+ return n;
+}
+
+static size_t
+sizeOfWxxxNode(FrameDataNode const *node)
+{
+ size_t n = 0;
+ if (node) {
+ n = 10; /* header size */
+ if (node->dsc.dim > 0) {
+ n += 1; /* text encoding flag */
+ switch (node->dsc.enc) {
+ default:
+ case 0:
+ n += 1 + node->dsc.dim;
+ break;
+ case 1:
+ n += 2 + node->dsc.dim * 2;
+ break;
+ }
+ }
+ if (node->txt.dim > 0) {
+ switch (node->txt.enc) {
+ default:
+ case 0:
+ n += node->txt.dim;
+ break;
+ case 1:
+ n += node->txt.dim - 1; /* UCS2 -> Latin1, skip BOM */
+ break;
+ }
+ }
+ }
+ return n;
+}
+
+static unsigned char *
+writeChars(unsigned char *frame, char const *str, size_t n)
+{
+ while (n--) {
+ *frame++ = *str++;
+ }
+ return frame;
+}
+
+static unsigned char *
+writeUcs2s(unsigned char *frame, unsigned short const *str, size_t n)
+{
+ if (n > 0) {
+ unsigned short const bom = *str;
+ while (n--) {
+ unsigned short const c = toLittleEndian(bom, *str++);
+ *frame++ = 0x00ffu & c;
+ *frame++ = 0x00ffu & (c >> 8);
+ }
+ }
+ return frame;
+}
+
+static unsigned char *
+writeLoBytes(unsigned char *frame, unsigned short const *str, size_t n)
+{
+ if (n > 0) {
+ unsigned short const bom = *str;
+ if (hasUcs2ByteOrderMarker(bom)) {
+ str++; n--; /* skip BOM */
+ }
+ while (n--) {
+ unsigned short const c = toLittleEndian(bom, *str++);
+ if (c < 0x0020u || 0x00ffu < c) {
+ *frame++ = 0x0020; /* blank */
+ }
+ else {
+ *frame++ = c;
+ }
+ }
+ }
+ return frame;
+}
+
+static unsigned char *
+set_frame_comment(unsigned char *frame, FrameDataNode const *node)
+{
+ size_t const n = sizeOfCommentNode(node);
+ if (n > 10) {
+ frame = set_4_byte_value(frame, node->fid);
+ frame = set_4_byte_value(frame, (uint32_t) (n - 10));
+ /* clear 2-byte header flags */
+ *frame++ = 0;
+ *frame++ = 0;
+ /* encoding descriptor byte */
+ *frame++ = node->txt.enc == 1 ? 1 : 0;
+ /* 3 bytes language */
+ *frame++ = node->lng[0];
+ *frame++ = node->lng[1];
+ *frame++ = node->lng[2];
+ /* descriptor with zero byte(s) separator */
+ if (node->dsc.enc != 1) {
+ frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
+ *frame++ = 0;
+ }
+ else {
+ frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
+ *frame++ = 0;
+ *frame++ = 0;
+ }
+ /* comment full text */
+ if (node->txt.enc != 1) {
+ frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
+ }
+ else {
+ frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
+ }
+ }
+ return frame;
+}
+
+static unsigned char *
+set_frame_custom2(unsigned char *frame, FrameDataNode const *node)
+{
+ size_t const n = sizeOfNode(node);
+ if (n > 10) {
+ frame = set_4_byte_value(frame, node->fid);
+ frame = set_4_byte_value(frame, (unsigned long) (n - 10));
+ /* clear 2-byte header flags */
+ *frame++ = 0;
+ *frame++ = 0;
+ /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
+ *frame++ = node->txt.enc == 1 ? 1 : 0;
+ if (node->dsc.dim > 0) {
+ if (node->dsc.enc != 1) {
+ frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
+ *frame++ = 0;
+ }
+ else {
+ frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
+ *frame++ = 0;
+ *frame++ = 0;
+ }
+ }
+ if (node->txt.enc != 1) {
+ frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
+ }
+ else {
+ frame = writeUcs2s(frame, node->txt.ptr.u, node->txt.dim);
+ }
+ }
+ return frame;
+}
+
+static unsigned char *
+set_frame_wxxx(unsigned char *frame, FrameDataNode const *node)
+{
+ size_t const n = sizeOfWxxxNode(node);
+ if (n > 10) {
+ frame = set_4_byte_value(frame, node->fid);
+ frame = set_4_byte_value(frame, (unsigned long) (n - 10));
+ /* clear 2-byte header flags */
+ *frame++ = 0;
+ *frame++ = 0;
+ if (node->dsc.dim > 0) {
+ /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
+ *frame++ = node->dsc.enc == 1 ? 1 : 0;
+ if (node->dsc.enc != 1) {
+ frame = writeChars(frame, node->dsc.ptr.l, node->dsc.dim);
+ *frame++ = 0;
+ }
+ else {
+ frame = writeUcs2s(frame, node->dsc.ptr.u, node->dsc.dim);
+ *frame++ = 0;
+ *frame++ = 0;
+ }
+ }
+ if (node->txt.enc != 1) {
+ frame = writeChars(frame, node->txt.ptr.l, node->txt.dim);
+ }
+ else {
+ frame = writeLoBytes(frame, node->txt.ptr.u, node->txt.dim);
+ }
+ }
+ return frame;
+}
+
+static unsigned char *
+set_frame_apic(unsigned char *frame, const char *mimetype, const unsigned char *data, size_t size)
+{
+ /* ID3v2.3 standard APIC frame:
+ *
+ * Text encoding $xx
+ * MIME type $00
+ * Picture type $xx
+ * Description $00 (00)
+ * Picture data
+ */
+ if (mimetype && data && size) {
+ frame = set_4_byte_value(frame, FRAME_ID('A', 'P', 'I', 'C'));
+ frame = set_4_byte_value(frame, (unsigned long) (4 + strlen(mimetype) + size));
+ /* clear 2-byte header flags */
+ *frame++ = 0;
+ *frame++ = 0;
+ /* clear 1 encoding descriptor byte to indicate ISO-8859-1 format */
+ *frame++ = 0;
+ /* copy mime_type */
+ while (*mimetype) {
+ *frame++ = *mimetype++;
+ }
+ *frame++ = 0;
+ /* set picture type to 0 */
+ *frame++ = 0;
+ /* empty description field */
+ *frame++ = 0;
+ /* copy the image data */
+ while (size--) {
+ *frame++ = *data++;
+ }
+ }
+ return frame;
+}
+
+int
+id3tag_set_fieldvalue(lame_t gfp, const char *fieldvalue)
+{
+ if (fieldvalue && *fieldvalue) {
+ if (strlen(fieldvalue) < 5 || fieldvalue[4] != '=') {
+ return -1;
+ }
+ return id3tag_set_textinfo_latin1(gfp, fieldvalue, &fieldvalue[5]);
+ }
+ return 0;
+}
+
+int
+id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue)
+{
+ if (fieldvalue && *fieldvalue) {
+ size_t dx = hasUcs2ByteOrderMarker(fieldvalue[0]);
+ unsigned short const separator = fromLatin1Char(fieldvalue, '=');
+ char fid[5] = {0,0,0,0,0};
+ uint32_t const frame_id = toID3v2TagId_ucs2(fieldvalue);
+ if (local_ucs2_strlen(fieldvalue) < (5+dx) || fieldvalue[4+dx] != separator) {
+ return -1;
+ }
+ fid[0] = (frame_id >> 24) & 0x0ff;
+ fid[1] = (frame_id >> 16) & 0x0ff;
+ fid[2] = (frame_id >> 8) & 0x0ff;
+ fid[3] = frame_id & 0x0ff;
+ if (frame_id != 0) {
+ unsigned short* txt = 0;
+ int rc;
+ local_ucs2_substr(&txt, fieldvalue, dx+5, local_ucs2_strlen(fieldvalue));
+ rc = id3tag_set_textinfo_utf16(gfp, fid, txt);
+ free(txt);
+ return rc;
+ }
+ }
+ return -1;
+}
+
+extern int
+id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue);
+
+int
+id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue)
+{
+ return id3tag_set_fieldvalue_utf16(gfp, fieldvalue);
+}
+
+size_t
+lame_get_id3v2_tag(lame_t gfp, unsigned char *buffer, size_t size)
+{
+ lame_internal_flags *gfc;
+ if (gfp == 0) {
+ return 0;
+ }
+ gfc = gfp->internal_flags;
+ if (gfc == 0) {
+ return 0;
+ }
+ if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
+ return 0;
+ }
+#if 0
+ debug_tag_spec_flags(gfc, "lame_get_id3v2_tag");
+#endif
+ {
+ int usev2 = test_tag_spec_flags(gfc, ADD_V2_FLAG | V2_ONLY_FLAG);
+ /* calculate length of four fields which may not fit in verion 1 tag */
+ size_t title_length = gfc->tag_spec.title ? strlen(gfc->tag_spec.title) : 0;
+ size_t artist_length = gfc->tag_spec.artist ? strlen(gfc->tag_spec.artist) : 0;
+ size_t album_length = gfc->tag_spec.album ? strlen(gfc->tag_spec.album) : 0;
+ size_t comment_length = gfc->tag_spec.comment ? strlen(gfc->tag_spec.comment) : 0;
+ /* write tag if explicitly requested or if fields overflow */
+ if ((title_length > 30)
+ || (artist_length > 30)
+ || (album_length > 30)
+ || (comment_length > 30)
+ || (gfc->tag_spec.track_id3v1 && (comment_length > 28))) {
+ usev2 = 1;
+ }
+ if (usev2) {
+ size_t tag_size;
+ unsigned char *p;
+ size_t adjusted_tag_size;
+ const char *albumart_mime = NULL;
+ static const char *mime_jpeg = "image/jpeg";
+ static const char *mime_png = "image/png";
+ static const char *mime_gif = "image/gif";
+
+ if (gfp->num_samples != MAX_U_32_NUM) {
+ id3v2AddAudioDuration(gfp, gfp->num_samples);
+ }
+
+ /* calulate size of tag starting with 10-byte tag header */
+ tag_size = 10;
+ if (gfc->tag_spec.albumart && gfc->tag_spec.albumart_size) {
+ switch (gfc->tag_spec.albumart_mimetype) {
+ case MIMETYPE_JPEG:
+ albumart_mime = mime_jpeg;
+ break;
+ case MIMETYPE_PNG:
+ albumart_mime = mime_png;
+ break;
+ case MIMETYPE_GIF:
+ albumart_mime = mime_gif;
+ break;
+ }
+ if (albumart_mime) {
+ tag_size += 10 + 4 + strlen(albumart_mime) + gfc->tag_spec.albumart_size;
+ }
+ }
+ {
+ id3tag_spec *tag = &gfc->tag_spec;
+ if (tag->v2_head != 0) {
+ FrameDataNode *node;
+ for (node = tag->v2_head; node != 0; node = node->nxt) {
+ if (node->fid == ID_COMMENT || node->fid == ID_USER) {
+ tag_size += sizeOfCommentNode(node);
+ }
+ else if (isFrameIdMatching(node->fid, FRAME_ID('W',0,0,0))) {
+ tag_size += sizeOfWxxxNode(node);
+ }
+ else {
+ tag_size += sizeOfNode(node);
+ }
+ }
+ }
+ }
+ if (test_tag_spec_flags(gfc, PAD_V2_FLAG)) {
+ /* add some bytes of padding */
+ tag_size += gfc->tag_spec.padding_size;
+ }
+ if (size < tag_size) {
+ return tag_size;
+ }
+ if (buffer == 0) {
+ return 0;
+ }
+ p = buffer;
+ /* set tag header starting with file identifier */
+ *p++ = 'I';
+ *p++ = 'D';
+ *p++ = '3';
+ /* set version number word */
+ *p++ = 3;
+ *p++ = 0;
+ /* clear flags byte */
+ *p++ = 0;
+ /* calculate and set tag size = total size - header size */
+ adjusted_tag_size = tag_size - 10;
+ /* encode adjusted size into four bytes where most significant
+ * bit is clear in each byte, for 28-bit total */
+ *p++ = (unsigned char) ((adjusted_tag_size >> 21) & 0x7fu);
+ *p++ = (unsigned char) ((adjusted_tag_size >> 14) & 0x7fu);
+ *p++ = (unsigned char) ((adjusted_tag_size >> 7) & 0x7fu);
+ *p++ = (unsigned char) (adjusted_tag_size & 0x7fu);
+
+ /*
+ * NOTE: The remainder of the tag (frames and padding, if any)
+ * are not "unsynchronized" to prevent false MPEG audio headers
+ * from appearing in the bitstream. Why? Well, most players
+ * and utilities know how to skip the ID3 version 2 tag by now
+ * even if they don't read its contents, and it's actually
+ * very unlikely that such a false "sync" pattern would occur
+ * in just the simple text frames added here.
+ */
+
+ /* set each frame in tag */
+ {
+ id3tag_spec *tag = &gfc->tag_spec;
+ if (tag->v2_head != 0) {
+ FrameDataNode *node;
+ for (node = tag->v2_head; node != 0; node = node->nxt) {
+ if (node->fid == ID_COMMENT || node->fid == ID_USER) {
+ p = set_frame_comment(p, node);
+ }
+ else if (isFrameIdMatching(node->fid,FRAME_ID('W',0,0,0))) {
+ p = set_frame_wxxx(p, node);
+ }
+ else {
+ p = set_frame_custom2(p, node);
+ }
+ }
+ }
+ }
+ if (albumart_mime) {
+ p = set_frame_apic(p, albumart_mime, gfc->tag_spec.albumart,
+ gfc->tag_spec.albumart_size);
+ }
+ /* clear any padding bytes */
+ memset(p, 0, tag_size - (p - buffer));
+ return tag_size;
+ }
+ }
+ return 0;
+}
+
+int
+id3tag_write_v2(lame_t gfp)
+{
+ lame_internal_flags *gfc = gfp->internal_flags;
+#if 0
+ debug_tag_spec_flags(gfc, "write v2");
+#endif
+ if (test_tag_spec_flags(gfc, V1_ONLY_FLAG)) {
+ return 0;
+ }
+ if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
+ unsigned char *tag = 0;
+ size_t tag_size, n;
+
+ n = lame_get_id3v2_tag(gfp, 0, 0);
+ tag = lame_calloc(unsigned char, n);
+ if (tag == 0) {
+ return -1;
+ }
+ tag_size = lame_get_id3v2_tag(gfp, tag, n);
+ if (tag_size > n) {
+ free(tag);
+ return -1;
+ }
+ else {
+ size_t i;
+ /* write tag directly into bitstream at current position */
+ for (i = 0; i < tag_size; ++i) {
+ add_dummy_byte(gfc, tag[i], 1);
+ }
+ }
+ free(tag);
+ return (int) tag_size; /* ok, tag should not exceed 2GB */
+ }
+ return 0;
+}
+
+static unsigned char *
+set_text_field(unsigned char *field, const char *text, size_t size, int pad)
+{
+ while (size--) {
+ if (text && *text) {
+ *field++ = *text++;
+ }
+ else {
+ *field++ = pad;
+ }
+ }
+ return field;
+}
+
+size_t
+lame_get_id3v1_tag(lame_t gfp, unsigned char *buffer, size_t size)
+{
+ size_t const tag_size = 128;
+ lame_internal_flags *gfc;
+
+ if (gfp == 0) {
+ return 0;
+ }
+ if (size < tag_size) {
+ return tag_size;
+ }
+ gfc = gfp->internal_flags;
+ if (gfc == 0) {
+ return 0;
+ }
+ if (buffer == 0) {
+ return 0;
+ }
+ if (test_tag_spec_flags(gfc, V2_ONLY_FLAG)) {
+ return 0;
+ }
+ if (test_tag_spec_flags(gfc, CHANGED_FLAG)) {
+ unsigned char *p = buffer;
+ int pad = test_tag_spec_flags(gfc, SPACE_V1_FLAG) ? ' ' : 0;
+ char year[5];
+
+ /* set tag identifier */
+ *p++ = 'T';
+ *p++ = 'A';
+ *p++ = 'G';
+ /* set each field in tag */
+ p = set_text_field(p, gfc->tag_spec.title, 30, pad);
+ p = set_text_field(p, gfc->tag_spec.artist, 30, pad);
+ p = set_text_field(p, gfc->tag_spec.album, 30, pad);
+ sprintf(year, "%d", gfc->tag_spec.year);
+ p = set_text_field(p, gfc->tag_spec.year ? year : NULL, 4, pad);
+ /* limit comment field to 28 bytes if a track is specified */
+ p = set_text_field(p, gfc->tag_spec.comment, gfc->tag_spec.track_id3v1 ? 28 : 30, pad);
+ if (gfc->tag_spec.track_id3v1) {
+ /* clear the next byte to indicate a version 1.1 tag */
+ *p++ = 0;
+ *p++ = gfc->tag_spec.track_id3v1;
+ }
+ *p++ = gfc->tag_spec.genre_id3v1;
+ return tag_size;
+ }
+ return 0;
+}
+
+int
+id3tag_write_v1(lame_t gfp)
+{
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ size_t i, n, m;
+ unsigned char tag[128];
+
+ m = sizeof(tag);
+ n = lame_get_id3v1_tag(gfp, tag, m);
+ if (n > m) {
+ return 0;
+ }
+ /* write tag directly into bitstream at current position */
+ for (i = 0; i < n; ++i) {
+ add_dummy_byte(gfc, tag[i], 1);
+ }
+ return (int) n; /* ok, tag has fixed size of 128 bytes, well below 2GB */
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/id3tag.h b/libnative/src/main/cpp/module/mp3/lame/id3tag.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac9c56e3610375ed946cc8dced69856a106ceda1
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/id3tag.h
@@ -0,0 +1,63 @@
+
+#ifndef LAME_ID3_H
+#define LAME_ID3_H
+
+
+#define CHANGED_FLAG (1U << 0)
+#define ADD_V2_FLAG (1U << 1)
+#define V1_ONLY_FLAG (1U << 2)
+#define V2_ONLY_FLAG (1U << 3)
+#define SPACE_V1_FLAG (1U << 4)
+#define PAD_V2_FLAG (1U << 5)
+
+enum {
+ MIMETYPE_NONE = 0,
+ MIMETYPE_JPEG,
+ MIMETYPE_PNG,
+ MIMETYPE_GIF,
+};
+
+typedef struct FrameDataNode {
+ struct FrameDataNode *nxt;
+ uint32_t fid; /* Frame Identifier */
+ char lng[4]; /* 3-character language descriptor */
+ struct {
+ union {
+ char *l; /* ptr to Latin-1 chars */
+ unsigned short *u; /* ptr to UCS-2 text */
+ unsigned char *b; /* ptr to raw bytes */
+ } ptr;
+ size_t dim;
+ int enc; /* 0:Latin-1, 1:UCS-2, 2:RAW */
+ } dsc , txt;
+} FrameDataNode;
+
+
+typedef struct id3tag_spec {
+ /* private data members */
+ unsigned int flags;
+ int year;
+ char *title;
+ char *artist;
+ char *album;
+ char *comment;
+ int track_id3v1;
+ int genre_id3v1;
+ unsigned char *albumart;
+ unsigned int albumart_size;
+ unsigned int padding_size;
+ int albumart_mimetype;
+ FrameDataNode *v2_head, *v2_tail;
+} id3tag_spec;
+
+
+/* write tag into stream at current position */
+extern int id3tag_write_v2(lame_global_flags * gfp);
+extern int id3tag_write_v1(lame_global_flags * gfp);
+/*
+ * NOTE: A version 2 tag will NOT be added unless one of the text fields won't
+ * fit in a version 1 tag (e.g. the title string is longer than 30 characters),
+ * or the "id3tag_add_v2" or "id3tag_v2_only" functions are used.
+ */
+
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/l3side.h b/libnative/src/main/cpp/module/mp3/lame/l3side.h
new file mode 100644
index 0000000000000000000000000000000000000000..f65bbedd8ee2d1ef839f83c73db6701ca6bf19f5
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/l3side.h
@@ -0,0 +1,95 @@
+/*
+ * Layer 3 side include file
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_L3SIDE_H
+#define LAME_L3SIDE_H
+
+/* max scalefactor band, max(SBMAX_l, SBMAX_s*3, (SBMAX_s-3)*3+8) */
+#define SFBMAX (SBMAX_s*3)
+
+/* Layer III side information. */
+typedef struct {
+ int l[1 + SBMAX_l];
+ int s[1 + SBMAX_s];
+ int psfb21[1 + PSFB21];
+ int psfb12[1 + PSFB12];
+} scalefac_struct;
+
+
+typedef struct {
+ FLOAT l[SBMAX_l];
+ FLOAT s[SBMAX_s][3];
+} III_psy_xmin;
+
+typedef struct {
+ III_psy_xmin thm;
+ III_psy_xmin en;
+} III_psy_ratio;
+
+typedef struct {
+ FLOAT xr[576];
+ int l3_enc[576];
+ int scalefac[SFBMAX];
+ FLOAT xrpow_max;
+
+ int part2_3_length;
+ int big_values;
+ int count1;
+ int global_gain;
+ int scalefac_compress;
+ int block_type;
+ int mixed_block_flag;
+ int table_select[3];
+ int subblock_gain[3 + 1];
+ int region0_count;
+ int region1_count;
+ int preflag;
+ int scalefac_scale;
+ int count1table_select;
+
+ int part2_length;
+ int sfb_lmax;
+ int sfb_smin;
+ int psy_lmax;
+ int sfbmax;
+ int psymax;
+ int sfbdivide;
+ int width[SFBMAX];
+ int window[SFBMAX];
+ int count1bits;
+ /* added for LSF */
+ const int *sfb_partition_table;
+ int slen[4];
+
+ int max_nonzero_coeff;
+ char energy_above_cutoff[SFBMAX];
+} gr_info;
+
+typedef struct {
+ gr_info tt[2][2];
+ int main_data_begin;
+ int private_bits;
+ int resvDrain_pre;
+ int resvDrain_post;
+ int scfsi[2][4];
+} III_side_info_t;
+
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/lame-analysis.h b/libnative/src/main/cpp/module/mp3/lame/lame-analysis.h
new file mode 100644
index 0000000000000000000000000000000000000000..5055a60364a78bfe7d8628b4cce011c500cbfe99
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lame-analysis.h
@@ -0,0 +1,96 @@
+/*
+ * GTK plotting routines source file
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_GTKANAL_H
+#define LAME_GTKANAL_H
+
+
+#define READ_AHEAD 40 /* number of frames to read ahead */
+#define MAXMPGLAG READ_AHEAD /* if the mpg123 lag becomes bigger than this
+ we have to stop */
+#define NUMBACK 6 /* number of frames we can back up */
+#define NUMPINFO (NUMBACK+READ_AHEAD+1)
+
+
+
+struct plotting_data {
+ int frameNum; /* current frame number */
+ int frameNum123;
+ int num_samples; /* number of pcm samples read for this frame */
+ double frametime; /* starting time of frame, in seconds */
+ double pcmdata[2][1600];
+ double pcmdata2[2][1152 + 1152 - DECDELAY];
+ double xr[2][2][576];
+ double mpg123xr[2][2][576];
+ double ms_ratio[2];
+ double ms_ener_ratio[2];
+
+ /* L,R, M and S values */
+ double energy_save[4][BLKSIZE]; /* psymodel is one ahead */
+ double energy[2][4][BLKSIZE];
+ double pe[2][4];
+ double thr[2][4][SBMAX_l];
+ double en[2][4][SBMAX_l];
+ double thr_s[2][4][3 * SBMAX_s];
+ double en_s[2][4][3 * SBMAX_s];
+ double ers_save[4]; /* psymodel is one ahead */
+ double ers[2][4];
+
+ double sfb[2][2][SBMAX_l];
+ double sfb_s[2][2][3 * SBMAX_s];
+ double LAMEsfb[2][2][SBMAX_l];
+ double LAMEsfb_s[2][2][3 * SBMAX_s];
+
+ int LAMEqss[2][2];
+ int qss[2][2];
+ int big_values[2][2];
+ int sub_gain[2][2][3];
+
+ double xfsf[2][2][SBMAX_l];
+ double xfsf_s[2][2][3 * SBMAX_s];
+
+ int over[2][2];
+ double tot_noise[2][2];
+ double max_noise[2][2];
+ double over_noise[2][2];
+ int over_SSD[2][2];
+ int blocktype[2][2];
+ int scalefac_scale[2][2];
+ int preflag[2][2];
+ int mpg123blocktype[2][2];
+ int mixed[2][2];
+ int mainbits[2][2];
+ int sfbits[2][2];
+ int LAMEmainbits[2][2];
+ int LAMEsfbits[2][2];
+ int framesize, stereo, js, ms_stereo, i_stereo, emph, bitrate, sampfreq, maindata;
+ int crc, padding;
+ int scfsi[2], mean_bits, resvsize;
+ int totbits;
+};
+#ifndef plotting_data_defined
+#define plotting_data_defined
+typedef struct plotting_data plotting_data;
+#endif
+#if 0
+extern plotting_data *pinfo;
+#endif
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/lame.c b/libnative/src/main/cpp/module/mp3/lame/lame.c
new file mode 100644
index 0000000000000000000000000000000000000000..5989160c37341ab6e02301520cd1f3d2d25039ac
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lame.c
@@ -0,0 +1,2614 @@
+/* -*- mode: C; mode: fold -*- */
+/*
+ * LAME MP3 encoding engine
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 2000-2005 Takehiro Tominaga
+ * Copyright (c) 2000-2011 Robert Hegemann
+ * Copyright (c) 2000-2005 Gabriel Bouvigne
+ * Copyright (c) 2000-2004 Alexander Leidinger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: lame.c,v 1.365 2011/10/18 21:51:20 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+
+#include "encoder.h"
+#include "util.h"
+#include "lame_global_flags.h"
+#include "gain_analysis.h"
+#include "bitstream.h"
+#include "quantize_pvt.h"
+#include "set_get.h"
+#include "quantize.h"
+#include "psymodel.h"
+#include "version.h"
+#include "VbrTag.h"
+#include "tables.h"
+
+
+#if defined(__FreeBSD__) && !defined(__alpha__)
+#include
+#endif
+#ifdef __riscos__
+#include "asmstuff.h"
+#endif
+
+#ifdef __sun__
+/* woraround for SunOS 4.x, it has SEEK_* defined here */
+#include
+#endif
+
+
+#define LAME_DEFAULT_QUALITY 3
+
+
+
+int
+is_lame_global_flags_valid(const lame_global_flags * gfp)
+{
+ if (gfp == NULL)
+ return 0;
+ if (gfp->class_id != LAME_ID)
+ return 0;
+ return 1;
+}
+
+
+int
+is_lame_internal_flags_valid(const lame_internal_flags * gfc)
+{
+ if (gfc == NULL)
+ return 0;
+ if (gfc->class_id != LAME_ID)
+ return 0;
+ return 1;
+}
+
+
+
+static FLOAT
+filter_coef(FLOAT x)
+{
+ if (x > 1.0)
+ return 0.0;
+ if (x <= 0.0)
+ return 1.0;
+
+ return cos(PI / 2 * x);
+}
+
+static void
+lame_init_params_ppflt(lame_internal_flags * gfc)
+{
+ SessionConfig_t *const cfg = &gfc->cfg;
+
+ /***************************************************************/
+ /* compute info needed for polyphase filter (filter type==0, default) */
+ /***************************************************************/
+
+ int band, maxband, minband;
+ FLOAT freq;
+ int lowpass_band = 32;
+ int highpass_band = -1;
+
+ if (cfg->lowpass1 > 0) {
+ minband = 999;
+ for (band = 0; band <= 31; band++) {
+ freq = band / 31.0;
+ /* this band and above will be zeroed: */
+ if (freq >= cfg->lowpass2) {
+ lowpass_band = Min(lowpass_band, band);
+ }
+ if (cfg->lowpass1 < freq && freq < cfg->lowpass2) {
+ minband = Min(minband, band);
+ }
+ }
+
+ /* compute the *actual* transition band implemented by
+ * the polyphase filter */
+ if (minband == 999) {
+ cfg->lowpass1 = (lowpass_band - .75) / 31.0;
+ }
+ else {
+ cfg->lowpass1 = (minband - .75) / 31.0;
+ }
+ cfg->lowpass2 = lowpass_band / 31.0;
+ }
+
+ /* make sure highpass filter is within 90% of what the effective
+ * highpass frequency will be */
+ if (cfg->highpass2 > 0) {
+ if (cfg->highpass2 < .9 * (.75 / 31.0)) {
+ cfg->highpass1 = 0;
+ cfg->highpass2 = 0;
+ MSGF(gfc, "Warning: highpass filter disabled. " "highpass frequency too small\n");
+ }
+ }
+
+ if (cfg->highpass2 > 0) {
+ maxband = -1;
+ for (band = 0; band <= 31; band++) {
+ freq = band / 31.0;
+ /* this band and below will be zereod */
+ if (freq <= cfg->highpass1) {
+ highpass_band = Max(highpass_band, band);
+ }
+ if (cfg->highpass1 < freq && freq < cfg->highpass2) {
+ maxband = Max(maxband, band);
+ }
+ }
+ /* compute the *actual* transition band implemented by
+ * the polyphase filter */
+ cfg->highpass1 = highpass_band / 31.0;
+ if (maxband == -1) {
+ cfg->highpass2 = (highpass_band + .75) / 31.0;
+ }
+ else {
+ cfg->highpass2 = (maxband + .75) / 31.0;
+ }
+ }
+
+ for (band = 0; band < 32; band++) {
+ FLOAT fc1, fc2;
+ freq = band / 31.0f;
+ if (cfg->highpass2 > cfg->highpass1) {
+ fc1 = filter_coef((cfg->highpass2 - freq) / (cfg->highpass2 - cfg->highpass1 + 1e-20));
+ }
+ else {
+ fc1 = 1.0f;
+ }
+ if (cfg->lowpass2 > cfg->lowpass1) {
+ fc2 = filter_coef((freq - cfg->lowpass1) / (cfg->lowpass2 - cfg->lowpass1 + 1e-20));
+ }
+ else {
+ fc2 = 1.0f;
+ }
+ gfc->sv_enc.amp_filter[band] = fc1 * fc2;
+ }
+}
+
+
+static void
+optimum_bandwidth(double *const lowerlimit, double *const upperlimit, const unsigned bitrate)
+{
+/*
+ * Input:
+ * bitrate total bitrate in kbps
+ *
+ * Output:
+ * lowerlimit: best lowpass frequency limit for input filter in Hz
+ * upperlimit: best highpass frequency limit for input filter in Hz
+ */
+ int table_index;
+
+ typedef struct {
+ int bitrate; /* only indicative value */
+ int lowpass;
+ } band_pass_t;
+
+ const band_pass_t freq_map[] = {
+ {8, 2000},
+ {16, 3700},
+ {24, 3900},
+ {32, 5500},
+ {40, 7000},
+ {48, 7500},
+ {56, 10000},
+ {64, 11000},
+ {80, 13500},
+ {96, 15100},
+ {112, 15600},
+ {128, 17000},
+ {160, 17500},
+ {192, 18600},
+ {224, 19400},
+ {256, 19700},
+ {320, 20500}
+ };
+
+
+ table_index = nearestBitrateFullIndex(bitrate);
+
+ (void) freq_map[table_index].bitrate;
+ *lowerlimit = freq_map[table_index].lowpass;
+
+
+/*
+ * Now we try to choose a good high pass filtering frequency.
+ * This value is currently not used.
+ * For fu < 16 kHz: sqrt(fu*fl) = 560 Hz
+ * For fu = 18 kHz: no high pass filtering
+ * This gives:
+ *
+ * 2 kHz => 160 Hz
+ * 3 kHz => 107 Hz
+ * 4 kHz => 80 Hz
+ * 8 kHz => 40 Hz
+ * 16 kHz => 20 Hz
+ * 17 kHz => 10 Hz
+ * 18 kHz => 0 Hz
+ *
+ * These are ad hoc values and these can be optimized if a high pass is available.
+ */
+/* if (f_low <= 16000)
+ f_high = 16000. * 20. / f_low;
+ else if (f_low <= 18000)
+ f_high = 180. - 0.01 * f_low;
+ else
+ f_high = 0.;*/
+
+ /*
+ * When we sometimes have a good highpass filter, we can add the highpass
+ * frequency to the lowpass frequency
+ */
+
+ /*if (upperlimit != NULL)
+ *upperlimit = f_high;*/
+ (void) upperlimit;
+}
+
+
+static int
+optimum_samplefreq(int lowpassfreq, int input_samplefreq)
+{
+/*
+ * Rules:
+ * - if possible, sfb21 should NOT be used
+ *
+ */
+ int suggested_samplefreq = 44100;
+
+ if (input_samplefreq >= 48000)
+ suggested_samplefreq = 48000;
+ else if (input_samplefreq >= 44100)
+ suggested_samplefreq = 44100;
+ else if (input_samplefreq >= 32000)
+ suggested_samplefreq = 32000;
+ else if (input_samplefreq >= 24000)
+ suggested_samplefreq = 24000;
+ else if (input_samplefreq >= 22050)
+ suggested_samplefreq = 22050;
+ else if (input_samplefreq >= 16000)
+ suggested_samplefreq = 16000;
+ else if (input_samplefreq >= 12000)
+ suggested_samplefreq = 12000;
+ else if (input_samplefreq >= 11025)
+ suggested_samplefreq = 11025;
+ else if (input_samplefreq >= 8000)
+ suggested_samplefreq = 8000;
+
+ if (lowpassfreq == -1)
+ return suggested_samplefreq;
+
+ if (lowpassfreq <= 15960)
+ suggested_samplefreq = 44100;
+ if (lowpassfreq <= 15250)
+ suggested_samplefreq = 32000;
+ if (lowpassfreq <= 11220)
+ suggested_samplefreq = 24000;
+ if (lowpassfreq <= 9970)
+ suggested_samplefreq = 22050;
+ if (lowpassfreq <= 7230)
+ suggested_samplefreq = 16000;
+ if (lowpassfreq <= 5420)
+ suggested_samplefreq = 12000;
+ if (lowpassfreq <= 4510)
+ suggested_samplefreq = 11025;
+ if (lowpassfreq <= 3970)
+ suggested_samplefreq = 8000;
+
+ if (input_samplefreq < suggested_samplefreq) {
+ /* choose a valid MPEG sample frequency above the input sample frequency
+ to avoid SFB21/12 bitrate bloat
+ rh 061115
+ */
+ if (input_samplefreq > 44100) {
+ return 48000;
+ }
+ if (input_samplefreq > 32000) {
+ return 44100;
+ }
+ if (input_samplefreq > 24000) {
+ return 32000;
+ }
+ if (input_samplefreq > 22050) {
+ return 24000;
+ }
+ if (input_samplefreq > 16000) {
+ return 22050;
+ }
+ if (input_samplefreq > 12000) {
+ return 16000;
+ }
+ if (input_samplefreq > 11025) {
+ return 12000;
+ }
+ if (input_samplefreq > 8000) {
+ return 11025;
+ }
+ return 8000;
+ }
+ return suggested_samplefreq;
+}
+
+
+
+
+
+/* set internal feature flags. USER should not access these since
+ * some combinations will produce strange results */
+static void
+lame_init_qval(lame_global_flags * gfp)
+{
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ SessionConfig_t *const cfg = &gfc->cfg;
+
+ switch (gfp->quality) {
+ default:
+ case 9: /* no psymodel, no noise shaping */
+ cfg->noise_shaping = 0;
+ cfg->noise_shaping_amp = 0;
+ cfg->noise_shaping_stop = 0;
+ cfg->use_best_huffman = 0;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 8:
+ gfp->quality = 7;
+ /*lint --fallthrough */
+ case 7: /* use psymodel (for short block and m/s switching), but no noise shapping */
+ cfg->noise_shaping = 0;
+ cfg->noise_shaping_amp = 0;
+ cfg->noise_shaping_stop = 0;
+ cfg->use_best_huffman = 0;
+ cfg->full_outer_loop = 0;
+ if (gfp->VBR == vbr_mt || gfp->VBR == vbr_mtrh) {
+ cfg->full_outer_loop = -1;
+ }
+ break;
+
+ case 6:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ cfg->noise_shaping_amp = 0;
+ cfg->noise_shaping_stop = 0;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 0;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 5:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ cfg->noise_shaping_amp = 0;
+ cfg->noise_shaping_stop = 0;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 0;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 4:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ cfg->noise_shaping_amp = 0;
+ cfg->noise_shaping_stop = 0;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 1;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 3:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ cfg->noise_shaping_amp = 1;
+ cfg->noise_shaping_stop = 1;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 1;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 2:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ if (gfc->sv_qnt.substep_shaping == 0)
+ gfc->sv_qnt.substep_shaping = 2;
+ cfg->noise_shaping_amp = 1;
+ cfg->noise_shaping_stop = 1;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 1; /* inner loop */
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 1:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ if (gfc->sv_qnt.substep_shaping == 0)
+ gfc->sv_qnt.substep_shaping = 2;
+ cfg->noise_shaping_amp = 2;
+ cfg->noise_shaping_stop = 1;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 1;
+ cfg->full_outer_loop = 0;
+ break;
+
+ case 0:
+ if (cfg->noise_shaping == 0)
+ cfg->noise_shaping = 1;
+ if (gfc->sv_qnt.substep_shaping == 0)
+ gfc->sv_qnt.substep_shaping = 2;
+ cfg->noise_shaping_amp = 2;
+ cfg->noise_shaping_stop = 1;
+ if (cfg->subblock_gain == -1)
+ cfg->subblock_gain = 1;
+ cfg->use_best_huffman = 1; /*type 2 disabled because of it slowness,
+ in favor of full outer loop search */
+ cfg->full_outer_loop = 1;
+ break;
+ }
+
+}
+
+
+
+static double
+linear_int(double a, double b, double m)
+{
+ return a + m * (b - a);
+}
+
+
+
+/********************************************************************
+ * initialize internal params based on data in gf
+ * (globalflags struct filled in by calling program)
+ *
+ * OUTLINE:
+ *
+ * We first have some complex code to determine bitrate,
+ * output samplerate and mode. It is complicated by the fact
+ * that we allow the user to set some or all of these parameters,
+ * and need to determine best possible values for the rest of them:
+ *
+ * 1. set some CPU related flags
+ * 2. check if we are mono->mono, stereo->mono or stereo->stereo
+ * 3. compute bitrate and output samplerate:
+ * user may have set compression ratio
+ * user may have set a bitrate
+ * user may have set a output samplerate
+ * 4. set some options which depend on output samplerate
+ * 5. compute the actual compression ratio
+ * 6. set mode based on compression ratio
+ *
+ * The remaining code is much simpler - it just sets options
+ * based on the mode & compression ratio:
+ *
+ * set allow_diff_short based on mode
+ * select lowpass filter based on compression ratio & mode
+ * set the bitrate index, and min/max bitrates for VBR modes
+ * disable VBR tag if it is not appropriate
+ * initialize the bitstream
+ * initialize scalefac_band data
+ * set sideinfo_len (based on channels, CRC, out_samplerate)
+ * write an id3v2 tag into the bitstream
+ * write VBR tag into the bitstream
+ * set mpeg1/2 flag
+ * estimate the number of frames (based on a lot of data)
+ *
+ * now we set more flags:
+ * nspsytune:
+ * see code
+ * VBR modes
+ * see code
+ * CBR/ABR
+ * see code
+ *
+ * Finally, we set the algorithm flags based on the gfp->quality value
+ * lame_init_qval(gfp);
+ *
+ ********************************************************************/
+int
+lame_init_params(lame_global_flags * gfp)
+{
+
+ int i;
+ int j;
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ SessionConfig_t *const cfg = &gfc->cfg;
+
+ gfc->class_id = 0;
+
+ cfg->enforce_min_bitrate = gfp->VBR_hard_min;
+ cfg->analysis = gfp->analysis;
+ if (cfg->analysis)
+ gfp->write_lame_tag = 0;
+
+ /* some file options not allowed if output is: not specified or stdout */
+ if (gfc->pinfo != NULL)
+ gfp->write_lame_tag = 0; /* disable Xing VBR tag */
+
+ /* report functions */
+ gfc->report_msg = gfp->report.msgf;
+ gfc->report_dbg = gfp->report.debugf;
+ gfc->report_err = gfp->report.errorf;
+
+ if (gfp->asm_optimizations.amd3dnow)
+ gfc->CPU_features.AMD_3DNow = has_3DNow();
+ else
+ gfc->CPU_features.AMD_3DNow = 0;
+
+ if (gfp->asm_optimizations.mmx)
+ gfc->CPU_features.MMX = has_MMX();
+ else
+ gfc->CPU_features.MMX = 0;
+
+ if (gfp->asm_optimizations.sse) {
+ gfc->CPU_features.SSE = has_SSE();
+ gfc->CPU_features.SSE2 = has_SSE2();
+ }
+ else {
+ gfc->CPU_features.SSE = 0;
+ gfc->CPU_features.SSE2 = 0;
+ }
+
+
+ if (NULL == gfc->ATH)
+ gfc->ATH = calloc(1, sizeof(ATH_t));
+
+ if (NULL == gfc->ATH)
+ return -2; /* maybe error codes should be enumerated in lame.h ?? */
+
+ if (NULL == gfc->sv_rpg.rgdata)
+ gfc->sv_rpg.rgdata = calloc(1, sizeof(replaygain_t));
+ if (NULL == gfc->sv_rpg.rgdata) {
+ freegfc(gfc);
+ gfp->internal_flags = NULL;
+ return -2;
+ }
+
+ cfg->error_protection = gfp->error_protection;
+ cfg->copyright = gfp->copyright;
+ cfg->original = gfp->original;
+ cfg->extension = gfp->extension;
+ cfg->emphasis = gfp->emphasis;
+
+ cfg->channels_in = gfp->num_channels;
+ if (cfg->channels_in == 1)
+ gfp->mode = MONO;
+ cfg->channels_out = (gfp->mode == MONO) ? 1 : 2;
+ if (gfp->mode == MONO)
+ gfp->force_ms = 0; /* don't allow forced mid/side stereo for mono output */
+ cfg->force_ms = gfp->force_ms;
+
+ if (gfp->VBR == vbr_off && gfp->VBR_mean_bitrate_kbps != 128 && gfp->brate == 0)
+ gfp->brate = gfp->VBR_mean_bitrate_kbps;
+
+ switch (gfp->VBR) {
+ case vbr_off:
+ case vbr_mtrh:
+ case vbr_mt:
+ /* these modes can handle free format condition */
+ break;
+ default:
+ gfp->free_format = 0; /* mode can't be mixed with free format */
+ break;
+ }
+
+ cfg->free_format = gfp->free_format;
+
+ if (gfp->VBR == vbr_off && gfp->brate == 0) {
+ /* no bitrate or compression ratio specified, use 11.025 */
+ if (EQ(gfp->compression_ratio, 0))
+ gfp->compression_ratio = 11.025; /* rate to compress a CD down to exactly 128000 bps */
+ }
+
+ /* find bitrate if user specify a compression ratio */
+ if (gfp->VBR == vbr_off && gfp->compression_ratio > 0) {
+
+ if (gfp->samplerate_out == 0)
+ gfp->samplerate_out = map2MP3Frequency((int) (0.97 * gfp->samplerate_in)); /* round up with a margin of 3% */
+
+ /* choose a bitrate for the output samplerate which achieves
+ * specified compression ratio
+ */
+ gfp->brate = gfp->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->compression_ratio);
+
+ /* we need the version for the bitrate table look up */
+ cfg->samplerate_index = SmpFrqIndex(gfp->samplerate_out, &cfg->version);
+
+ if (!cfg->free_format) /* for non Free Format find the nearest allowed bitrate */
+ gfp->brate = FindNearestBitrate(gfp->brate, cfg->version, gfp->samplerate_out);
+ }
+ if (gfp->samplerate_out) {
+ if (gfp->samplerate_out < 16000) {
+ gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 8);
+ gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 64);
+ }
+ else if (gfp->samplerate_out < 32000) {
+ gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 8);
+ gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 160);
+ }
+ else {
+ gfp->VBR_mean_bitrate_kbps = Max(gfp->VBR_mean_bitrate_kbps, 32);
+ gfp->VBR_mean_bitrate_kbps = Min(gfp->VBR_mean_bitrate_kbps, 320);
+ }
+ }
+ /* WORK IN PROGRESS */
+ /* mapping VBR scale to internal VBR quality settings */
+ if (gfp->samplerate_out == 0 && (gfp->VBR == vbr_mt || gfp->VBR == vbr_mtrh)) {
+ float const qval = gfp->VBR_q + gfp->VBR_q_frac;
+ struct q_map { int sr_a; float qa, qb, ta, tb; int lp; };
+ struct q_map const m[9]
+ = { {48000, 0.0,6.5, 0.0,6.5, 23700}
+ , {44100, 0.0,6.5, 0.0,6.5, 21780}
+ , {32000, 6.5,8.0, 5.2,6.5, 15800}
+ , {24000, 8.0,8.5, 5.2,6.0, 11850}
+ , {22050, 8.5,9.01, 5.2,6.5, 10892}
+ , {16000, 9.01,9.4, 4.9,6.5, 7903}
+ , {12000, 9.4,9.6, 4.5,6.0, 5928}
+ , {11025, 9.6,9.9, 5.1,6.5, 5446}
+ , { 8000, 9.9,10., 4.9,6.5, 3952}
+ };
+ for (i = 2; i < 9; ++i) {
+ if (gfp->samplerate_in == m[i].sr_a) {
+ if (qval < m[i].qa) {
+ double d = qval / m[i].qa;
+ d = d * m[i].ta;
+ gfp->VBR_q = (int)d;
+ gfp->VBR_q_frac = d - gfp->VBR_q;
+ }
+ }
+ if (gfp->samplerate_in >= m[i].sr_a) {
+ if (m[i].qa <= qval && qval < m[i].qb) {
+ float const q_ = m[i].qb-m[i].qa;
+ float const t_ = m[i].tb-m[i].ta;
+ double d = m[i].ta + t_ * (qval-m[i].qa) / q_;
+ gfp->VBR_q = (int)d;
+ gfp->VBR_q_frac = d - gfp->VBR_q;
+ gfp->samplerate_out = m[i].sr_a;
+ if (gfp->lowpassfreq == 0) {
+ gfp->lowpassfreq = -1;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /****************************************************************/
+ /* if a filter has not been enabled, see if we should add one: */
+ /****************************************************************/
+ if (gfp->lowpassfreq == 0) {
+ double lowpass = 16000;
+ double highpass;
+
+ switch (gfp->VBR) {
+ case vbr_off:{
+ optimum_bandwidth(&lowpass, &highpass, gfp->brate);
+ break;
+ }
+ case vbr_abr:{
+ optimum_bandwidth(&lowpass, &highpass, gfp->VBR_mean_bitrate_kbps);
+ break;
+ }
+ case vbr_rh:{
+ int const x[11] = {
+ 19500, 19000, 18600, 18000, 17500, 16000, 15600, 14900, 12500, 10000, 3950
+ };
+ if (0 <= gfp->VBR_q && gfp->VBR_q <= 9) {
+ double a = x[gfp->VBR_q], b = x[gfp->VBR_q + 1], m = gfp->VBR_q_frac;
+ lowpass = linear_int(a, b, m);
+ }
+ else {
+ lowpass = 19500;
+ }
+ break;
+ }
+ case vbr_mtrh:
+ case vbr_mt:{
+ int const x[11] = {
+ 24000, 19500, 18500, 18000, 17500, 17000, 16500, 15600, 15200, 7230, 3950
+ };
+ if (0 <= gfp->VBR_q && gfp->VBR_q <= 9) {
+ double a = x[gfp->VBR_q], b = x[gfp->VBR_q + 1], m = gfp->VBR_q_frac;
+ lowpass = linear_int(a, b, m);
+ }
+ else {
+ lowpass = 21500;
+ }
+ break;
+ }
+ default:{
+ int const x[11] = {
+ 19500, 19000, 18500, 18000, 17500, 16500, 15500, 14500, 12500, 9500, 3950
+ };
+ if (0 <= gfp->VBR_q && gfp->VBR_q <= 9) {
+ double a = x[gfp->VBR_q], b = x[gfp->VBR_q + 1], m = gfp->VBR_q_frac;
+ lowpass = linear_int(a, b, m);
+ }
+ else {
+ lowpass = 19500;
+ }
+ }
+ }
+
+ if (gfp->mode == MONO && (gfp->VBR == vbr_off || gfp->VBR == vbr_abr))
+ lowpass *= 1.5;
+
+ gfp->lowpassfreq = lowpass;
+ }
+
+ if (gfp->samplerate_out == 0) {
+ if (2 * gfp->lowpassfreq > gfp->samplerate_in) {
+ gfp->lowpassfreq = gfp->samplerate_in / 2;
+ }
+ gfp->samplerate_out = optimum_samplefreq((int) gfp->lowpassfreq, gfp->samplerate_in);
+ }
+ if (gfp->VBR == vbr_mt || gfp->VBR == vbr_mtrh) {
+ gfp->lowpassfreq = Min(24000, gfp->lowpassfreq);
+ }
+ else {
+ gfp->lowpassfreq = Min(20500, gfp->lowpassfreq);
+ }
+ gfp->lowpassfreq = Min(gfp->samplerate_out / 2, gfp->lowpassfreq);
+
+ if (gfp->VBR == vbr_off) {
+ gfp->compression_ratio = gfp->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->brate);
+ }
+ if (gfp->VBR == vbr_abr) {
+ gfp->compression_ratio =
+ gfp->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->VBR_mean_bitrate_kbps);
+ }
+
+ /* do not compute ReplayGain values and do not find the peak sample
+ if we can't store them */
+ if (!gfp->write_lame_tag) {
+ gfp->findReplayGain = 0;
+ gfp->decode_on_the_fly = 0;
+ cfg->findPeakSample = 0;
+ }
+
+ cfg->findReplayGain = gfp->findReplayGain;
+ cfg->decode_on_the_fly = gfp->decode_on_the_fly;
+
+ if (cfg->decode_on_the_fly)
+ cfg->findPeakSample = 1;
+
+ if (cfg->findReplayGain) {
+ if (InitGainAnalysis(gfc->sv_rpg.rgdata, gfp->samplerate_out) == INIT_GAIN_ANALYSIS_ERROR) {
+ freegfc(gfc);
+ gfp->internal_flags = NULL;
+ return -6;
+ }
+ }
+
+#ifdef DECODE_ON_THE_FLY
+ if (cfg->decode_on_the_fly && !gfp->decode_only) {
+ if (gfc->hip) {
+ hip_decode_exit(gfc->hip);
+ }
+ gfc->hip = hip_decode_init();
+ /* report functions */
+ hip_set_errorf(gfc->hip, gfp->report.errorf);
+ hip_set_debugf(gfc->hip, gfp->report.debugf);
+ hip_set_msgf(gfc->hip, gfp->report.msgf);
+ }
+#endif
+
+ cfg->disable_reservoir = gfp->disable_reservoir;
+ cfg->lowpassfreq = gfp->lowpassfreq;
+ cfg->highpassfreq = gfp->highpassfreq;
+ cfg->samplerate_in = gfp->samplerate_in;
+ cfg->samplerate_out = gfp->samplerate_out;
+ cfg->mode_gr = cfg->samplerate_out <= 24000 ? 1 : 2; /* Number of granules per frame */
+ gfc->ov_enc.encoder_delay = ENCDELAY;
+
+
+ /*
+ * sample freq bitrate compression ratio
+ * [kHz] [kbps/channel] for 16 bit input
+ * 44.1 56 12.6
+ * 44.1 64 11.025
+ * 44.1 80 8.82
+ * 22.05 24 14.7
+ * 22.05 32 11.025
+ * 22.05 40 8.82
+ * 16 16 16.0
+ * 16 24 10.667
+ *
+ */
+ /*
+ * For VBR, take a guess at the compression_ratio.
+ * For example:
+ *
+ * VBR_q compression like
+ * - 4.4 320 kbps/44 kHz
+ * 0...1 5.5 256 kbps/44 kHz
+ * 2 7.3 192 kbps/44 kHz
+ * 4 8.8 160 kbps/44 kHz
+ * 6 11 128 kbps/44 kHz
+ * 9 14.7 96 kbps
+ *
+ * for lower bitrates, downsample with --resample
+ */
+
+ switch (gfp->VBR) {
+ case vbr_mt:
+ case vbr_rh:
+ case vbr_mtrh:
+ {
+ /*numbers are a bit strange, but they determine the lowpass value */
+ FLOAT const cmp[] = { 5.7, 6.5, 7.3, 8.2, 10, 11.9, 13, 14, 15, 16.5 };
+ gfp->compression_ratio = cmp[gfp->VBR_q];
+ }
+ break;
+ case vbr_abr:
+ gfp->compression_ratio =
+ cfg->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->VBR_mean_bitrate_kbps);
+ break;
+ default:
+ gfp->compression_ratio = cfg->samplerate_out * 16 * cfg->channels_out / (1.e3 * gfp->brate);
+ break;
+ }
+
+
+ /* mode = -1 (not set by user) or
+ * mode = MONO (because of only 1 input channel).
+ * If mode has not been set, then select J-STEREO
+ */
+ if (gfp->mode == NOT_SET) {
+ gfp->mode = JOINT_STEREO;
+ }
+
+ cfg->mode = gfp->mode;
+
+
+ /* apply user driven high pass filter */
+ if (cfg->highpassfreq > 0) {
+ cfg->highpass1 = 2. * cfg->highpassfreq;
+
+ if (gfp->highpasswidth >= 0)
+ cfg->highpass2 = 2. * (cfg->highpassfreq + gfp->highpasswidth);
+ else /* 0% above on default */
+ cfg->highpass2 = (1 + 0.00) * 2. * cfg->highpassfreq;
+
+ cfg->highpass1 /= cfg->samplerate_out;
+ cfg->highpass2 /= cfg->samplerate_out;
+ }
+ else {
+ cfg->highpass1 = 0;
+ cfg->highpass2 = 0;
+ }
+ /* apply user driven low pass filter */
+ cfg->lowpass1 = 0;
+ cfg->lowpass2 = 0;
+ if (cfg->lowpassfreq > 0 && cfg->lowpassfreq < (cfg->samplerate_out / 2) ) {
+ cfg->lowpass2 = 2. * cfg->lowpassfreq;
+ if (gfp->lowpasswidth >= 0) {
+ cfg->lowpass1 = 2. * (cfg->lowpassfreq - gfp->lowpasswidth);
+ if (cfg->lowpass1 < 0) /* has to be >= 0 */
+ cfg->lowpass1 = 0;
+ }
+ else { /* 0% below on default */
+ cfg->lowpass1 = (1 - 0.00) * 2. * cfg->lowpassfreq;
+ }
+ cfg->lowpass1 /= cfg->samplerate_out;
+ cfg->lowpass2 /= cfg->samplerate_out;
+ }
+
+
+
+
+ /**********************************************************************/
+ /* compute info needed for polyphase filter (filter type==0, default) */
+ /**********************************************************************/
+ lame_init_params_ppflt(gfc);
+
+
+ /*******************************************************
+ * samplerate and bitrate index
+ *******************************************************/
+ cfg->samplerate_index = SmpFrqIndex(cfg->samplerate_out, &cfg->version);
+ if (cfg->samplerate_index < 0) {
+ freegfc(gfc);
+ gfp->internal_flags = NULL;
+ return -1;
+ }
+
+ if (gfp->VBR == vbr_off) {
+ if (cfg->free_format) {
+ gfc->ov_enc.bitrate_index = 0;
+ }
+ else {
+ gfp->brate = FindNearestBitrate(gfp->brate, cfg->version, cfg->samplerate_out);
+ gfc->ov_enc.bitrate_index = BitrateIndex(gfp->brate, cfg->version, cfg->samplerate_out);
+ if (gfc->ov_enc.bitrate_index <= 0) {
+ freegfc(gfc);
+ gfp->internal_flags = NULL;
+ return -1;
+ }
+ }
+ }
+ else {
+ gfc->ov_enc.bitrate_index = 1;
+ }
+
+ init_bit_stream_w(gfc);
+
+ j = cfg->samplerate_index + (3 * cfg->version) + 6 * (cfg->samplerate_out < 16000);
+ for (i = 0; i < SBMAX_l + 1; i++)
+ gfc->scalefac_band.l[i] = sfBandIndex[j].l[i];
+
+ for (i = 0; i < PSFB21 + 1; i++) {
+ int const size = (gfc->scalefac_band.l[22] - gfc->scalefac_band.l[21]) / PSFB21;
+ int const start = gfc->scalefac_band.l[21] + i * size;
+ gfc->scalefac_band.psfb21[i] = start;
+ }
+ gfc->scalefac_band.psfb21[PSFB21] = 576;
+
+ for (i = 0; i < SBMAX_s + 1; i++)
+ gfc->scalefac_band.s[i] = sfBandIndex[j].s[i];
+
+ for (i = 0; i < PSFB12 + 1; i++) {
+ int const size = (gfc->scalefac_band.s[13] - gfc->scalefac_band.s[12]) / PSFB12;
+ int const start = gfc->scalefac_band.s[12] + i * size;
+ gfc->scalefac_band.psfb12[i] = start;
+ }
+ gfc->scalefac_band.psfb12[PSFB12] = 192;
+
+ /* determine the mean bitrate for main data */
+ if (cfg->mode_gr == 2) /* MPEG 1 */
+ cfg->sideinfo_len = (cfg->channels_out == 1) ? 4 + 17 : 4 + 32;
+ else /* MPEG 2 */
+ cfg->sideinfo_len = (cfg->channels_out == 1) ? 4 + 9 : 4 + 17;
+
+ if (cfg->error_protection)
+ cfg->sideinfo_len += 2;
+
+ gfc->class_id = LAME_ID;
+
+ {
+ int k;
+
+ for (k = 0; k < 19; k++)
+ gfc->sv_enc.pefirbuf[k] = 700 * cfg->mode_gr * cfg->channels_out;
+
+ if (gfp->ATHtype == -1)
+ gfp->ATHtype = 4;
+ }
+
+ assert(gfp->VBR_q <= 9);
+ assert(gfp->VBR_q >= 0);
+
+ switch (gfp->VBR) {
+
+ case vbr_mt:
+ case vbr_mtrh:{
+ if (gfp->strict_ISO < 0) {
+ gfp->strict_ISO = MDB_MAXIMUM;
+ }
+ if (gfp->useTemporal < 0) {
+ gfp->useTemporal = 0; /* off by default for this VBR mode */
+ }
+
+ (void) apply_preset(gfp, 500 - (gfp->VBR_q * 10), 0);
+ /* The newer VBR code supports only a limited
+ subset of quality levels:
+ 9-5=5 are the same, uses x^3/4 quantization
+ 4-0=0 are the same 5 plus best huffman divide code
+ */
+ if (gfp->quality < 0)
+ gfp->quality = LAME_DEFAULT_QUALITY;
+ if (gfp->quality < 5)
+ gfp->quality = 0;
+ if (gfp->quality > 7)
+ gfp->quality = 7;
+
+ /* sfb21 extra only with MPEG-1 at higher sampling rates
+ */
+ if (gfp->experimentalY)
+ gfc->sv_qnt.sfb21_extra = 0;
+ else
+ gfc->sv_qnt.sfb21_extra = (cfg->samplerate_out > 44000);
+
+ gfc->iteration_loop = VBR_new_iteration_loop;
+ break;
+
+ }
+ case vbr_rh:{
+
+ (void) apply_preset(gfp, 500 - (gfp->VBR_q * 10), 0);
+
+ /* sfb21 extra only with MPEG-1 at higher sampling rates
+ */
+ if (gfp->experimentalY)
+ gfc->sv_qnt.sfb21_extra = 0;
+ else
+ gfc->sv_qnt.sfb21_extra = (cfg->samplerate_out > 44000);
+
+ /* VBR needs at least the output of GPSYCHO,
+ * so we have to garantee that by setting a minimum
+ * quality level, actually level 6 does it.
+ * down to level 6
+ */
+ if (gfp->quality > 6)
+ gfp->quality = 6;
+
+
+ if (gfp->quality < 0)
+ gfp->quality = LAME_DEFAULT_QUALITY;
+
+ gfc->iteration_loop = VBR_old_iteration_loop;
+ break;
+ }
+
+ default: /* cbr/abr */ {
+ vbr_mode vbrmode;
+
+ /* no sfb21 extra with CBR code
+ */
+ gfc->sv_qnt.sfb21_extra = 0;
+
+ if (gfp->quality < 0)
+ gfp->quality = LAME_DEFAULT_QUALITY;
+
+
+ vbrmode = gfp->VBR;
+ if (vbrmode == vbr_off)
+ (void) lame_set_VBR_mean_bitrate_kbps(gfp, gfp->brate);
+ /* second, set parameters depending on bitrate */
+ (void) apply_preset(gfp, gfp->VBR_mean_bitrate_kbps, 0);
+ gfp->VBR = vbrmode;
+
+ if (vbrmode == vbr_off) {
+ gfc->iteration_loop = CBR_iteration_loop;
+ }
+ else {
+ gfc->iteration_loop = ABR_iteration_loop;
+ }
+ break;
+ }
+ }
+
+ /*initialize default values common for all modes */
+
+ gfc->sv_qnt.mask_adjust = gfp->maskingadjust;
+ gfc->sv_qnt.mask_adjust_short = gfp->maskingadjust_short;
+
+ /* just another daily changing developer switch */
+ if (gfp->tune) {
+ gfc->sv_qnt.mask_adjust += gfp->tune_value_a;
+ gfc->sv_qnt.mask_adjust_short += gfp->tune_value_a;
+ }
+
+
+ if (gfp->VBR != vbr_off) { /* choose a min/max bitrate for VBR */
+ /* if the user didn't specify VBR_max_bitrate: */
+ cfg->vbr_min_bitrate_index = 1; /* default: allow 8 kbps (MPEG-2) or 32 kbps (MPEG-1) */
+ cfg->vbr_max_bitrate_index = 14; /* default: allow 160 kbps (MPEG-2) or 320 kbps (MPEG-1) */
+ if (cfg->samplerate_out < 16000)
+ cfg->vbr_max_bitrate_index = 8; /* default: allow 64 kbps (MPEG-2.5) */
+ if (gfp->VBR_min_bitrate_kbps) {
+ gfp->VBR_min_bitrate_kbps =
+ FindNearestBitrate(gfp->VBR_min_bitrate_kbps, cfg->version, cfg->samplerate_out);
+ cfg->vbr_min_bitrate_index =
+ BitrateIndex(gfp->VBR_min_bitrate_kbps, cfg->version, cfg->samplerate_out);
+ if (cfg->vbr_min_bitrate_index < 0)
+ return -1;
+ }
+ if (gfp->VBR_max_bitrate_kbps) {
+ gfp->VBR_max_bitrate_kbps =
+ FindNearestBitrate(gfp->VBR_max_bitrate_kbps, cfg->version, cfg->samplerate_out);
+ cfg->vbr_max_bitrate_index =
+ BitrateIndex(gfp->VBR_max_bitrate_kbps, cfg->version, cfg->samplerate_out);
+ if (cfg->vbr_max_bitrate_index < 0)
+ return -1;
+ }
+ gfp->VBR_min_bitrate_kbps = bitrate_table[cfg->version][cfg->vbr_min_bitrate_index];
+ gfp->VBR_max_bitrate_kbps = bitrate_table[cfg->version][cfg->vbr_max_bitrate_index];
+ gfp->VBR_mean_bitrate_kbps =
+ Min(bitrate_table[cfg->version][cfg->vbr_max_bitrate_index],
+ gfp->VBR_mean_bitrate_kbps);
+ gfp->VBR_mean_bitrate_kbps =
+ Max(bitrate_table[cfg->version][cfg->vbr_min_bitrate_index],
+ gfp->VBR_mean_bitrate_kbps);
+ }
+
+ cfg->preset = gfp->preset;
+ cfg->write_lame_tag = gfp->write_lame_tag;
+ cfg->vbr = gfp->VBR;
+ gfc->sv_qnt.substep_shaping = gfp->substep_shaping;
+ cfg->noise_shaping = gfp->noise_shaping;
+ cfg->subblock_gain = gfp->subblock_gain;
+ cfg->use_best_huffman = gfp->use_best_huffman;
+ cfg->avg_bitrate = gfp->brate;
+ cfg->vbr_avg_bitrate_kbps = gfp->VBR_mean_bitrate_kbps;
+ cfg->compression_ratio = gfp->compression_ratio;
+
+ /* initialize internal qval settings */
+ lame_init_qval(gfp);
+
+
+ /* automatic ATH adjustment on
+ */
+ if (gfp->athaa_type < 0)
+ gfc->ATH->use_adjust = 3;
+ else
+ gfc->ATH->use_adjust = gfp->athaa_type;
+
+
+ /* initialize internal adaptive ATH settings -jd */
+ gfc->ATH->aa_sensitivity_p = pow(10.0, gfp->athaa_sensitivity / -10.0);
+
+
+ if (gfp->short_blocks == short_block_not_set) {
+ gfp->short_blocks = short_block_allowed;
+ }
+
+ /*Note Jan/2003: Many hardware decoders cannot handle short blocks in regular
+ stereo mode unless they are coupled (same type in both channels)
+ it is a rare event (1 frame per min. or so) that LAME would use
+ uncoupled short blocks, so lets turn them off until we decide
+ how to handle this. No other encoders allow uncoupled short blocks,
+ even though it is in the standard. */
+ /* rh 20040217: coupling makes no sense for mono and dual-mono streams
+ */
+ if (gfp->short_blocks == short_block_allowed
+ && (cfg->mode == JOINT_STEREO || cfg->mode == STEREO)) {
+ gfp->short_blocks = short_block_coupled;
+ }
+
+ cfg->short_blocks = gfp->short_blocks;
+
+
+ if (lame_get_quant_comp(gfp) < 0)
+ (void) lame_set_quant_comp(gfp, 1);
+ if (lame_get_quant_comp_short(gfp) < 0)
+ (void) lame_set_quant_comp_short(gfp, 0);
+
+ if (lame_get_msfix(gfp) < 0)
+ lame_set_msfix(gfp, 0);
+
+ /* select psychoacoustic model */
+ (void) lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | 1);
+
+ if (gfp->ATHtype < 0)
+ gfp->ATHtype = 4;
+
+ if (gfp->ATHcurve < 0)
+ gfp->ATHcurve = 4;
+
+ if (gfp->interChRatio < 0)
+ gfp->interChRatio = 0;
+
+ if (gfp->useTemporal < 0)
+ gfp->useTemporal = 1; /* on by default */
+
+
+ cfg->interChRatio = gfp->interChRatio;
+ cfg->msfix = gfp->msfix;
+ cfg->ATH_offset_db = 0-gfp->ATH_lower_db;
+ cfg->ATH_offset_factor = powf(10.f, cfg->ATH_offset_db * 0.1f);
+ cfg->ATHcurve = gfp->ATHcurve;
+ cfg->ATHtype = gfp->ATHtype;
+ cfg->ATHonly = gfp->ATHonly;
+ cfg->ATHshort = gfp->ATHshort;
+ cfg->noATH = gfp->noATH;
+
+ cfg->quant_comp = gfp->quant_comp;
+ cfg->quant_comp_short = gfp->quant_comp_short;
+
+ cfg->use_temporal_masking_effect = gfp->useTemporal;
+ cfg->use_safe_joint_stereo = gfp->exp_nspsytune & 2;
+ {
+ cfg->adjust_bass_db = (gfp->exp_nspsytune >> 2) & 63;
+ if (cfg->adjust_bass_db >= 32.f)
+ cfg->adjust_bass_db -= 64.f;
+ cfg->adjust_bass_db *= 0.25f;
+
+ cfg->adjust_alto_db = (gfp->exp_nspsytune >> 8) & 63;
+ if (cfg->adjust_alto_db >= 32.f)
+ cfg->adjust_alto_db -= 64.f;
+ cfg->adjust_alto_db *= 0.25f;
+
+ cfg->adjust_treble_db = (gfp->exp_nspsytune >> 14) & 63;
+ if (cfg->adjust_treble_db >= 32.f)
+ cfg->adjust_treble_db -= 64.f;
+ cfg->adjust_treble_db *= 0.25f;
+
+ /* to be compatible with Naoki's original code, the next 6 bits
+ * define only the amount of changing treble for sfb21 */
+ cfg->adjust_sfb21_db = (gfp->exp_nspsytune >> 20) & 63;
+ if (cfg->adjust_sfb21_db >= 32.f)
+ cfg->adjust_sfb21_db -= 64.f;
+ cfg->adjust_sfb21_db *= 0.25f;
+ cfg->adjust_sfb21_db += cfg->adjust_treble_db;
+ }
+
+ /* Setting up the PCM input data transform matrix, to apply
+ * user defined re-scaling, and or two-to-one channel downmix.
+ */
+ {
+ FLOAT m[2][2] = { {1.0f, 0.0f}, {0.0f, 1.0f} };
+
+ /* user selected scaling of the samples */
+ m[0][0] *= gfp->scale;
+ m[0][1] *= gfp->scale;
+ m[1][0] *= gfp->scale;
+ m[1][1] *= gfp->scale;
+ /* user selected scaling of the channel 0 (left) samples */
+ m[0][0] *= gfp->scale_left;
+ m[0][1] *= gfp->scale_left;
+ /* user selected scaling of the channel 1 (right) samples */
+ m[1][0] *= gfp->scale_right;
+ m[1][1] *= gfp->scale_right;
+ /* Downsample to Mono if 2 channels in and 1 channel out */
+ if (cfg->channels_in == 2 && cfg->channels_out == 1) {
+ m[0][0] = 0.5f * (m[0][0] + m[1][0]);
+ m[0][1] = 0.5f * (m[0][1] + m[1][1]);
+ m[1][0] = 0;
+ m[1][1] = 0;
+ }
+ cfg->pcm_transform[0][0] = m[0][0];
+ cfg->pcm_transform[0][1] = m[0][1];
+ cfg->pcm_transform[1][0] = m[1][0];
+ cfg->pcm_transform[1][1] = m[1][1];
+ }
+
+ /* padding method as described in
+ * "MPEG-Layer3 / Bitstream Syntax and Decoding"
+ * by Martin Sieler, Ralph Sperschneider
+ *
+ * note: there is no padding for the very first frame
+ *
+ * Robert Hegemann 2000-06-22
+ */
+ gfc->sv_enc.slot_lag = gfc->sv_enc.frac_SpF = 0;
+ if (cfg->vbr == vbr_off)
+ gfc->sv_enc.slot_lag = gfc->sv_enc.frac_SpF
+ = ((cfg->version + 1) * 72000L * cfg->avg_bitrate) % cfg->samplerate_out;
+
+ (void) lame_init_bitstream(gfp);
+
+ iteration_init(gfc);
+ (void) psymodel_init(gfp);
+
+ cfg->buffer_constraint = get_max_frame_buffer_size_by_constraint(cfg, gfp->strict_ISO);
+ return 0;
+}
+
+static void
+concatSep(char* dest, char const* sep, char const* str)
+{
+ if (*dest != 0) strcat(dest, sep);
+ strcat(dest, str);
+}
+
+/*
+ * print_config
+ *
+ * Prints some selected information about the coding parameters via
+ * the macro command MSGF(), which is currently mapped to lame_errorf
+ * (reports via a error function?), which is a printf-like function
+ * for .
+ */
+
+void
+lame_print_config(const lame_global_flags * gfp)
+{
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ double const out_samplerate = cfg->samplerate_out;
+ double const in_samplerate = cfg->samplerate_in;
+
+ MSGF(gfc, "LAME %s %s (%s)\n", get_lame_version(), get_lame_os_bitness(), get_lame_url());
+
+#if (LAME_ALPHA_VERSION)
+ MSGF(gfc, "warning: alpha versions should be used for testing only\n");
+#endif
+ if (gfc->CPU_features.MMX
+ || gfc->CPU_features.AMD_3DNow || gfc->CPU_features.SSE || gfc->CPU_features.SSE2) {
+ char text[256] = { 0 };
+ int fft_asm_used = 0;
+#ifdef HAVE_NASM
+ if (gfc->CPU_features.AMD_3DNow) {
+ fft_asm_used = 1;
+ }
+ else if (gfc->CPU_features.SSE) {
+ fft_asm_used = 2;
+ }
+#else
+# if defined( HAVE_XMMINTRIN_H ) && defined( MIN_ARCH_SSE )
+ {
+ fft_asm_used = 3;
+ }
+# endif
+#endif
+ if (gfc->CPU_features.MMX) {
+#ifdef MMX_choose_table
+ concatSep(text, ", ", "MMX (ASM used)");
+#else
+ concatSep(text, ", ", "MMX");
+#endif
+ }
+ if (gfc->CPU_features.AMD_3DNow) {
+ concatSep(text, ", ", (fft_asm_used == 1) ? "3DNow! (ASM used)" : "3DNow!");
+ }
+ if (gfc->CPU_features.SSE) {
+#if defined(HAVE_XMMINTRIN_H)
+ concatSep(text, ", ", "SSE (ASM used)");
+#else
+ concatSep(text, ", ", (fft_asm_used == 2) ? "SSE (ASM used)" : "SSE");
+#endif
+ }
+ if (gfc->CPU_features.SSE2) {
+ concatSep(text, ", ", (fft_asm_used == 3) ? "SSE2 (ASM used)" : "SSE2");
+ }
+ MSGF(gfc, "CPU features: %s\n", text);
+ }
+
+ if (cfg->channels_in == 2 && cfg->channels_out == 1 /* mono */ ) {
+ MSGF(gfc, "Autoconverting from stereo to mono. Setting encoding to mono mode.\n");
+ }
+
+ if (isResamplingNecessary(cfg)) {
+ MSGF(gfc, "Resampling: input %g kHz output %g kHz\n",
+ 1.e-3 * in_samplerate, 1.e-3 * out_samplerate);
+ }
+
+ if (cfg->highpass2 > 0.)
+ MSGF(gfc,
+ "Using polyphase highpass filter, transition band: %5.0f Hz - %5.0f Hz\n",
+ 0.5 * cfg->highpass1 * out_samplerate, 0.5 * cfg->highpass2 * out_samplerate);
+ if (0. < cfg->lowpass1 || 0. < cfg->lowpass2) {
+ MSGF(gfc,
+ "Using polyphase lowpass filter, transition band: %5.0f Hz - %5.0f Hz\n",
+ 0.5 * cfg->lowpass1 * out_samplerate, 0.5 * cfg->lowpass2 * out_samplerate);
+ }
+ else {
+ MSGF(gfc, "polyphase lowpass filter disabled\n");
+ }
+
+ if (cfg->free_format) {
+ MSGF(gfc, "Warning: many decoders cannot handle free format bitstreams\n");
+ if (cfg->avg_bitrate > 320) {
+ MSGF(gfc,
+ "Warning: many decoders cannot handle free format bitrates >320 kbps (see documentation)\n");
+ }
+ }
+}
+
+
+/** rh:
+ * some pretty printing is very welcome at this point!
+ * so, if someone is willing to do so, please do it!
+ * add more, if you see more...
+ */
+void
+lame_print_internals(const lame_global_flags * gfp)
+{
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ const char *pc = "";
+
+ /* compiler/processor optimizations, operational, etc.
+ */
+ MSGF(gfc, "\nmisc:\n\n");
+
+ MSGF(gfc, "\tscaling: %g\n", gfp->scale);
+ MSGF(gfc, "\tch0 (left) scaling: %g\n", gfp->scale_left);
+ MSGF(gfc, "\tch1 (right) scaling: %g\n", gfp->scale_right);
+ switch (cfg->use_best_huffman) {
+ default:
+ pc = "normal";
+ break;
+ case 1:
+ pc = "best (outside loop)";
+ break;
+ case 2:
+ pc = "best (inside loop, slow)";
+ break;
+ }
+ MSGF(gfc, "\thuffman search: %s\n", pc);
+ MSGF(gfc, "\texperimental Y=%d\n", gfp->experimentalY);
+ MSGF(gfc, "\t...\n");
+
+ /* everything controlling the stream format
+ */
+ MSGF(gfc, "\nstream format:\n\n");
+ switch (cfg->version) {
+ case 0:
+ pc = "2.5";
+ break;
+ case 1:
+ pc = "1";
+ break;
+ case 2:
+ pc = "2";
+ break;
+ default:
+ pc = "?";
+ break;
+ }
+ MSGF(gfc, "\tMPEG-%s Layer 3\n", pc);
+ switch (cfg->mode) {
+ case JOINT_STEREO:
+ pc = "joint stereo";
+ break;
+ case STEREO:
+ pc = "stereo";
+ break;
+ case DUAL_CHANNEL:
+ pc = "dual channel";
+ break;
+ case MONO:
+ pc = "mono";
+ break;
+ case NOT_SET:
+ pc = "not set (error)";
+ break;
+ default:
+ pc = "unknown (error)";
+ break;
+ }
+ MSGF(gfc, "\t%d channel - %s\n", cfg->channels_out, pc);
+
+ switch (cfg->vbr) {
+ case vbr_off:
+ pc = "off";
+ break;
+ default:
+ pc = "all";
+ break;
+ }
+ MSGF(gfc, "\tpadding: %s\n", pc);
+
+ if (vbr_default == cfg->vbr)
+ pc = "(default)";
+ else if (cfg->free_format)
+ pc = "(free format)";
+ else
+ pc = "";
+ switch (cfg->vbr) {
+ case vbr_off:
+ MSGF(gfc, "\tconstant bitrate - CBR %s\n", pc);
+ break;
+ case vbr_abr:
+ MSGF(gfc, "\tvariable bitrate - ABR %s\n", pc);
+ break;
+ case vbr_rh:
+ MSGF(gfc, "\tvariable bitrate - VBR rh %s\n", pc);
+ break;
+ case vbr_mt:
+ MSGF(gfc, "\tvariable bitrate - VBR mt %s\n", pc);
+ break;
+ case vbr_mtrh:
+ MSGF(gfc, "\tvariable bitrate - VBR mtrh %s\n", pc);
+ break;
+ default:
+ MSGF(gfc, "\t ?? oops, some new one ?? \n");
+ break;
+ }
+ if (cfg->write_lame_tag)
+ MSGF(gfc, "\tusing LAME Tag\n");
+ MSGF(gfc, "\t...\n");
+
+ /* everything controlling psychoacoustic settings, like ATH, etc.
+ */
+ MSGF(gfc, "\npsychoacoustic:\n\n");
+
+ switch (cfg->short_blocks) {
+ default:
+ case short_block_not_set:
+ pc = "?";
+ break;
+ case short_block_allowed:
+ pc = "allowed";
+ break;
+ case short_block_coupled:
+ pc = "channel coupled";
+ break;
+ case short_block_dispensed:
+ pc = "dispensed";
+ break;
+ case short_block_forced:
+ pc = "forced";
+ break;
+ }
+ MSGF(gfc, "\tusing short blocks: %s\n", pc);
+ MSGF(gfc, "\tsubblock gain: %d\n", cfg->subblock_gain);
+ MSGF(gfc, "\tadjust masking: %g dB\n", gfc->sv_qnt.mask_adjust);
+ MSGF(gfc, "\tadjust masking short: %g dB\n", gfc->sv_qnt.mask_adjust_short);
+ MSGF(gfc, "\tquantization comparison: %d\n", cfg->quant_comp);
+ MSGF(gfc, "\t ^ comparison short blocks: %d\n", cfg->quant_comp_short);
+ MSGF(gfc, "\tnoise shaping: %d\n", cfg->noise_shaping);
+ MSGF(gfc, "\t ^ amplification: %d\n", cfg->noise_shaping_amp);
+ MSGF(gfc, "\t ^ stopping: %d\n", cfg->noise_shaping_stop);
+
+ pc = "using";
+ if (cfg->ATHshort)
+ pc = "the only masking for short blocks";
+ if (cfg->ATHonly)
+ pc = "the only masking";
+ if (cfg->noATH)
+ pc = "not used";
+ MSGF(gfc, "\tATH: %s\n", pc);
+ MSGF(gfc, "\t ^ type: %d\n", cfg->ATHtype);
+ MSGF(gfc, "\t ^ shape: %g%s\n", cfg->ATHcurve, " (only for type 4)");
+ MSGF(gfc, "\t ^ level adjustement: %g dB\n", cfg->ATH_offset_db);
+ MSGF(gfc, "\t ^ adjust type: %d\n", gfc->ATH->use_adjust);
+ MSGF(gfc, "\t ^ adjust sensitivity power: %f\n", gfc->ATH->aa_sensitivity_p);
+
+ MSGF(gfc, "\texperimental psy tunings by Naoki Shibata\n");
+ MSGF(gfc, "\t adjust masking bass=%g dB, alto=%g dB, treble=%g dB, sfb21=%g dB\n",
+ 10 * log10(gfc->sv_qnt.longfact[0]),
+ 10 * log10(gfc->sv_qnt.longfact[7]),
+ 10 * log10(gfc->sv_qnt.longfact[14]), 10 * log10(gfc->sv_qnt.longfact[21]));
+
+ pc = cfg->use_temporal_masking_effect ? "yes" : "no";
+ MSGF(gfc, "\tusing temporal masking effect: %s\n", pc);
+ MSGF(gfc, "\tinterchannel masking ratio: %g\n", cfg->interChRatio);
+ MSGF(gfc, "\t...\n");
+
+ /* that's all ?
+ */
+ MSGF(gfc, "\n");
+ return;
+}
+
+
+static void
+save_gain_values(lame_internal_flags * gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ RpgStateVar_t const *const rsv = &gfc->sv_rpg;
+ RpgResult_t *const rov = &gfc->ov_rpg;
+ /* save the ReplayGain value */
+ if (cfg->findReplayGain) {
+ FLOAT const RadioGain = (FLOAT) GetTitleGain(rsv->rgdata);
+ if (NEQ(RadioGain, GAIN_NOT_ENOUGH_SAMPLES)) {
+ rov->RadioGain = (int) floor(RadioGain * 10.0 + 0.5); /* round to nearest */
+ }
+ else {
+ rov->RadioGain = 0;
+ }
+ }
+
+ /* find the gain and scale change required for no clipping */
+ if (cfg->findPeakSample) {
+ rov->noclipGainChange = (int) ceil(log10(rov->PeakSample / 32767.0) * 20.0 * 10.0); /* round up */
+
+ if (rov->noclipGainChange > 0) { /* clipping occurs */
+ rov->noclipScale = floor((32767.0f / rov->PeakSample) * 100.0f) / 100.0f; /* round down */
+ }
+ else /* no clipping */
+ rov->noclipScale = -1.0f;
+ }
+}
+
+
+
+static int
+update_inbuffer_size(lame_internal_flags * gfc, const int nsamples)
+{
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ if (esv->in_buffer_0 == 0 || esv->in_buffer_nsamples < nsamples) {
+ if (esv->in_buffer_0) {
+ free(esv->in_buffer_0);
+ }
+ if (esv->in_buffer_1) {
+ free(esv->in_buffer_1);
+ }
+ esv->in_buffer_0 = calloc(nsamples, sizeof(sample_t));
+ esv->in_buffer_1 = calloc(nsamples, sizeof(sample_t));
+ esv->in_buffer_nsamples = nsamples;
+ }
+ if (esv->in_buffer_0 == NULL || esv->in_buffer_1 == NULL) {
+ if (esv->in_buffer_0) {
+ free(esv->in_buffer_0);
+ }
+ if (esv->in_buffer_1) {
+ free(esv->in_buffer_1);
+ }
+ esv->in_buffer_0 = 0;
+ esv->in_buffer_1 = 0;
+ esv->in_buffer_nsamples = 0;
+ ERRORF(gfc, "Error: can't allocate in_buffer buffer\n");
+ return -2;
+ }
+ return 0;
+}
+
+
+static int
+calcNeeded(SessionConfig_t const * cfg)
+{
+ int mf_needed;
+ int pcm_samples_per_frame = 576 * cfg->mode_gr;
+
+ /* some sanity checks */
+#if ENCDELAY < MDCTDELAY
+# error ENCDELAY is less than MDCTDELAY, see encoder.h
+#endif
+#if FFTOFFSET > BLKSIZE
+# error FFTOFFSET is greater than BLKSIZE, see encoder.h
+#endif
+
+ mf_needed = BLKSIZE + pcm_samples_per_frame - FFTOFFSET; /* amount needed for FFT */
+ /*mf_needed = Max(mf_needed, 286 + 576 * (1 + gfc->mode_gr)); */
+ mf_needed = Max(mf_needed, 512 + pcm_samples_per_frame - 32);
+
+ assert(MFSIZE >= mf_needed);
+
+ return mf_needed;
+}
+
+
+/*
+ * THE MAIN LAME ENCODING INTERFACE
+ * mt 3/00
+ *
+ * input pcm data, output (maybe) mp3 frames.
+ * This routine handles all buffering, resampling and filtering for you.
+ * The required mp3buffer_size can be computed from num_samples,
+ * samplerate and encoding rate, but here is a worst case estimate:
+ *
+ * mp3buffer_size in bytes = 1.25*num_samples + 7200
+ *
+ * return code = number of bytes output in mp3buffer. can be 0
+ *
+ * NOTE: this routine uses LAME's internal PCM data representation,
+ * 'sample_t'. It should not be used by any application.
+ * applications should use lame_encode_buffer(),
+ * lame_encode_buffer_float()
+ * lame_encode_buffer_int()
+ * etc... depending on what type of data they are working with.
+*/
+static int
+lame_encode_buffer_sample_t(lame_internal_flags * gfc,
+ int nsamples, unsigned char *mp3buf, const int mp3buf_size)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int pcm_samples_per_frame = 576 * cfg->mode_gr;
+ int mp3size = 0, ret, i, ch, mf_needed;
+ int mp3out;
+ sample_t *mfbuf[2];
+ sample_t *in_buffer[2];
+
+ if (gfc->class_id != LAME_ID)
+ return -3;
+
+ if (nsamples == 0)
+ return 0;
+
+ /* copy out any tags that may have been written into bitstream */
+ mp3out = copy_buffer(gfc, mp3buf, mp3buf_size, 0);
+ if (mp3out < 0)
+ return mp3out; /* not enough buffer space */
+ mp3buf += mp3out;
+ mp3size += mp3out;
+
+ in_buffer[0] = esv->in_buffer_0;
+ in_buffer[1] = esv->in_buffer_1;
+
+ mf_needed = calcNeeded(cfg);
+
+ mfbuf[0] = esv->mfbuf[0];
+ mfbuf[1] = esv->mfbuf[1];
+
+ while (nsamples > 0) {
+ sample_t const *in_buffer_ptr[2];
+ int n_in = 0; /* number of input samples processed with fill_buffer */
+ int n_out = 0; /* number of samples output with fill_buffer */
+ /* n_in <> n_out if we are resampling */
+
+ in_buffer_ptr[0] = in_buffer[0];
+ in_buffer_ptr[1] = in_buffer[1];
+ /* copy in new samples into mfbuf, with resampling */
+ fill_buffer(gfc, mfbuf, &in_buffer_ptr[0], nsamples, &n_in, &n_out);
+
+ /* compute ReplayGain of resampled input if requested */
+ if (cfg->findReplayGain && !cfg->decode_on_the_fly)
+ if (AnalyzeSamples
+ (gfc->sv_rpg.rgdata, &mfbuf[0][esv->mf_size], &mfbuf[1][esv->mf_size], n_out,
+ cfg->channels_out) == GAIN_ANALYSIS_ERROR)
+ return -6;
+
+
+
+ /* update in_buffer counters */
+ nsamples -= n_in;
+ in_buffer[0] += n_in;
+ if (cfg->channels_out == 2)
+ in_buffer[1] += n_in;
+
+ /* update mfbuf[] counters */
+ esv->mf_size += n_out;
+ assert(esv->mf_size <= MFSIZE);
+
+ /* lame_encode_flush may have set gfc->mf_sample_to_encode to 0
+ * so we have to reinitialize it here when that happened.
+ */
+ if (esv->mf_samples_to_encode < 1) {
+ esv->mf_samples_to_encode = ENCDELAY + POSTDELAY;
+ }
+ esv->mf_samples_to_encode += n_out;
+
+
+ if (esv->mf_size >= mf_needed) {
+ /* encode the frame. */
+ /* mp3buf = pointer to current location in buffer */
+ /* mp3buf_size = size of original mp3 output buffer */
+ /* = 0 if we should not worry about the */
+ /* buffer size because calling program is */
+ /* to lazy to compute it */
+ /* mp3size = size of data written to buffer so far */
+ /* mp3buf_size-mp3size = amount of space avalable */
+
+ int buf_size = mp3buf_size - mp3size;
+ if (mp3buf_size == 0)
+ buf_size = 0;
+
+ ret = lame_encode_mp3_frame(gfc, mfbuf[0], mfbuf[1], mp3buf, buf_size);
+
+ if (ret < 0)
+ return ret;
+ mp3buf += ret;
+ mp3size += ret;
+
+ /* shift out old samples */
+ esv->mf_size -= pcm_samples_per_frame;
+ esv->mf_samples_to_encode -= pcm_samples_per_frame;
+ for (ch = 0; ch < cfg->channels_out; ch++)
+ for (i = 0; i < esv->mf_size; i++)
+ mfbuf[ch][i] = mfbuf[ch][i + pcm_samples_per_frame];
+ }
+ }
+ assert(nsamples == 0);
+
+ return mp3size;
+}
+
+enum PCMSampleType
+{ pcm_short_type
+, pcm_int_type
+, pcm_long_type
+, pcm_float_type
+, pcm_double_type
+};
+
+static void
+lame_copy_inbuffer(lame_internal_flags* gfc,
+ void const* l, void const* r, int nsamples,
+ enum PCMSampleType pcm_type, int jump, FLOAT s)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ sample_t* ib0 = esv->in_buffer_0;
+ sample_t* ib1 = esv->in_buffer_1;
+ FLOAT m[2][2];
+
+ /* Apply user defined re-scaling */
+ m[0][0] = s * cfg->pcm_transform[0][0];
+ m[0][1] = s * cfg->pcm_transform[0][1];
+ m[1][0] = s * cfg->pcm_transform[1][0];
+ m[1][1] = s * cfg->pcm_transform[1][1];
+
+ /* make a copy of input buffer, changing type to sample_t */
+#define COPY_AND_TRANSFORM(T) \
+{ \
+ T const *bl = l, *br = r; \
+ int i; \
+ for (i = 0; i < nsamples; i++) { \
+ sample_t const xl = *bl; \
+ sample_t const xr = *br; \
+ sample_t const u = xl * m[0][0] + xr * m[0][1]; \
+ sample_t const v = xl * m[1][0] + xr * m[1][1]; \
+ ib0[i] = u; \
+ ib1[i] = v; \
+ bl += jump; \
+ br += jump; \
+ } \
+}
+ switch ( pcm_type ) {
+ case pcm_short_type:
+ COPY_AND_TRANSFORM(short int);
+ break;
+ case pcm_int_type:
+ COPY_AND_TRANSFORM(int);
+ break;
+ case pcm_long_type:
+ COPY_AND_TRANSFORM(long int);
+ break;
+ case pcm_float_type:
+ COPY_AND_TRANSFORM(float);
+ break;
+ case pcm_double_type:
+ COPY_AND_TRANSFORM(double);
+ break;
+ }
+}
+
+
+static int
+lame_encode_buffer_template(lame_global_flags * gfp,
+ void const* buffer_l, void const* buffer_r, const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size, enum PCMSampleType pcm_type, int aa, FLOAT norm)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+
+ if (nsamples == 0)
+ return 0;
+
+ if (update_inbuffer_size(gfc, nsamples) != 0) {
+ return -2;
+ }
+ /* make a copy of input buffer, changing type to sample_t */
+ if (cfg->channels_in > 1) {
+ if (buffer_l == 0 || buffer_r == 0) {
+ return 0;
+ }
+ lame_copy_inbuffer(gfc, buffer_l, buffer_r, nsamples, pcm_type, aa, norm);
+ }
+ else {
+ if (buffer_l == 0) {
+ return 0;
+ }
+ lame_copy_inbuffer(gfc, buffer_l, buffer_l, nsamples, pcm_type, aa, norm);
+ }
+
+ return lame_encode_buffer_sample_t(gfc, nsamples, mp3buf, mp3buf_size);
+ }
+ }
+ return -3;
+}
+
+int
+lame_encode_buffer(lame_global_flags * gfp,
+ const short int pcm_l[], const short int pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_short_type, 1, 1.0);
+}
+
+
+int
+lame_encode_buffer_float(lame_global_flags * gfp,
+ const float pcm_l[], const float pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 32768 for full scale */
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_float_type, 1, 1.0);
+}
+
+
+int
+lame_encode_buffer_ieee_float(lame_t gfp,
+ const float pcm_l[], const float pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 1.0 for full scale */
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_float_type, 1, 32767.0);
+}
+
+
+int
+lame_encode_buffer_interleaved_ieee_float(lame_t gfp,
+ const float pcm[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 1.0 for full scale */
+ return lame_encode_buffer_template(gfp, pcm, pcm+1, nsamples, mp3buf, mp3buf_size, pcm_float_type, 2, 32767.0);
+}
+
+
+int
+lame_encode_buffer_ieee_double(lame_t gfp,
+ const double pcm_l[], const double pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 1.0 for full scale */
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_double_type, 1, 32767.0);
+}
+
+
+int
+lame_encode_buffer_interleaved_ieee_double(lame_t gfp,
+ const double pcm[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 1.0 for full scale */
+ return lame_encode_buffer_template(gfp, pcm, pcm+1, nsamples, mp3buf, mp3buf_size, pcm_double_type, 2, 32767.0);
+}
+
+
+int
+lame_encode_buffer_int(lame_global_flags * gfp,
+ const int pcm_l[], const int pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- MAX_INT for full scale */
+ FLOAT const norm = (1.0 / (1L << (8 * sizeof(int) - 16)));
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_int_type, 1, norm);
+}
+
+
+int
+lame_encode_buffer_long2(lame_global_flags * gfp,
+ const long pcm_l[], const long pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- MAX_LONG for full scale */
+ FLOAT const norm = (1.0 / (1L << (8 * sizeof(long) - 16)));
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_long_type, 1, norm);
+}
+
+
+int
+lame_encode_buffer_long(lame_global_flags * gfp,
+ const long pcm_l[], const long pcm_r[], const int nsamples,
+ unsigned char *mp3buf, const int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- 32768 for full scale */
+ return lame_encode_buffer_template(gfp, pcm_l, pcm_r, nsamples, mp3buf, mp3buf_size, pcm_long_type, 1, 1.0);
+}
+
+
+
+int
+lame_encode_buffer_interleaved(lame_global_flags * gfp,
+ short int pcm[], int nsamples,
+ unsigned char *mp3buf, int mp3buf_size)
+{
+ /* input is assumed to be normalized to +/- MAX_SHORT for full scale */
+ return lame_encode_buffer_template(gfp, pcm, pcm+1, nsamples, mp3buf, mp3buf_size, pcm_short_type, 2, 1.0);
+}
+
+
+
+
+/*****************************************************************
+ Flush mp3 buffer, pad with ancillary data so last frame is complete.
+ Reset reservoir size to 0
+ but keep all PCM samples and MDCT data in memory
+ This option is used to break a large file into several mp3 files
+ that when concatenated together will decode with no gaps
+ Because we set the reservoir=0, they will also decode seperately
+ with no errors.
+*********************************************************************/
+int
+lame_encode_flush_nogap(lame_global_flags * gfp, unsigned char *mp3buffer, int mp3buffer_size)
+{
+ int rc = -3;
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ flush_bitstream(gfc);
+ rc = copy_buffer(gfc, mp3buffer, mp3buffer_size, 1);
+ save_gain_values(gfc);
+ }
+ }
+ return rc;
+}
+
+
+/* called by lame_init_params. You can also call this after flush_nogap
+ if you want to write new id3v2 and Xing VBR tags into the bitstream */
+int
+lame_init_bitstream(lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ if (gfc != 0) {
+ gfc->ov_enc.frame_number = 0;
+
+ if (gfp->write_id3tag_automatic) {
+ (void) id3tag_write_v2(gfp);
+ }
+ /* initialize histogram data optionally used by frontend */
+ memset(gfc->ov_enc.bitrate_channelmode_hist, 0,
+ sizeof(gfc->ov_enc.bitrate_channelmode_hist));
+ memset(gfc->ov_enc.bitrate_blocktype_hist, 0,
+ sizeof(gfc->ov_enc.bitrate_blocktype_hist));
+
+ gfc->ov_rpg.PeakSample = 0.0;
+
+ /* Write initial VBR Header to bitstream and init VBR data */
+ if (gfc->cfg.write_lame_tag)
+ (void) InitVbrTag(gfp);
+
+
+ return 0;
+ }
+ }
+ return -3;
+}
+
+
+/*****************************************************************/
+/* flush internal PCM sample buffers, then mp3 buffers */
+/* then write id3 v1 tags into bitstream. */
+/*****************************************************************/
+
+int
+lame_encode_flush(lame_global_flags * gfp, unsigned char *mp3buffer, int mp3buffer_size)
+{
+ lame_internal_flags *gfc;
+ SessionConfig_t const *cfg;
+ EncStateVar_t *esv;
+ short int buffer[2][1152];
+ int imp3 = 0, mp3count, mp3buffer_size_remaining;
+
+ /* we always add POSTDELAY=288 padding to make sure granule with real
+ * data can be complety decoded (because of 50% overlap with next granule */
+ int end_padding;
+ int frames_left;
+ int samples_to_encode;
+ int pcm_samples_per_frame;
+ int mf_needed;
+ int is_resampling_necessary;
+ double resample_ratio = 1;
+
+ if (!is_lame_global_flags_valid(gfp)) {
+ return -3;
+ }
+ gfc = gfp->internal_flags;
+ if (!is_lame_internal_flags_valid(gfc)) {
+ return -3;
+ }
+ cfg = &gfc->cfg;
+ esv = &gfc->sv_enc;
+
+ /* Was flush already called? */
+ if (esv->mf_samples_to_encode < 1) {
+ return 0;
+ }
+ pcm_samples_per_frame = 576 * cfg->mode_gr;
+ mf_needed = calcNeeded(cfg);
+
+ samples_to_encode = esv->mf_samples_to_encode - POSTDELAY;
+
+ memset(buffer, 0, sizeof(buffer));
+ mp3count = 0;
+
+ is_resampling_necessary = isResamplingNecessary(cfg);
+ if (is_resampling_necessary) {
+ resample_ratio = (double)cfg->samplerate_in / (double)cfg->samplerate_out;
+ /* delay due to resampling; needs to be fixed, if resampling code gets changed */
+ samples_to_encode += 16. / resample_ratio;
+ }
+ end_padding = pcm_samples_per_frame - (samples_to_encode % pcm_samples_per_frame);
+ if (end_padding < 576)
+ end_padding += pcm_samples_per_frame;
+ gfc->ov_enc.encoder_padding = end_padding;
+
+ frames_left = (samples_to_encode + end_padding) / pcm_samples_per_frame;
+ while (frames_left > 0 && imp3 >= 0) {
+ int const frame_num = gfc->ov_enc.frame_number;
+ int bunch = mf_needed - esv->mf_size;
+
+ bunch *= resample_ratio;
+ if (bunch > 1152) bunch = 1152;
+ if (bunch < 1) bunch = 1;
+
+ mp3buffer_size_remaining = mp3buffer_size - mp3count;
+
+ /* if user specifed buffer size = 0, dont check size */
+ if (mp3buffer_size == 0)
+ mp3buffer_size_remaining = 0;
+
+ /* send in a frame of 0 padding until all internal sample buffers
+ * are flushed
+ */
+ imp3 = lame_encode_buffer(gfp, buffer[0], buffer[1], bunch,
+ mp3buffer, mp3buffer_size_remaining);
+
+ mp3buffer += imp3;
+ mp3count += imp3;
+ frames_left -= ((frame_num != gfc->ov_enc.frame_number) ? 1 : 0);
+ }
+ /* Set esv->mf_samples_to_encode to 0, so we may detect
+ * and break loops calling it more than once in a row.
+ */
+ esv->mf_samples_to_encode = 0;
+
+ if (imp3 < 0) {
+ /* some type of fatal error */
+ return imp3;
+ }
+
+ mp3buffer_size_remaining = mp3buffer_size - mp3count;
+ /* if user specifed buffer size = 0, dont check size */
+ if (mp3buffer_size == 0)
+ mp3buffer_size_remaining = 0;
+
+ /* mp3 related stuff. bit buffer might still contain some mp3 data */
+ flush_bitstream(gfc);
+ imp3 = copy_buffer(gfc, mp3buffer, mp3buffer_size_remaining, 1);
+ save_gain_values(gfc);
+ if (imp3 < 0) {
+ /* some type of fatal error */
+ return imp3;
+ }
+ mp3buffer += imp3;
+ mp3count += imp3;
+ mp3buffer_size_remaining = mp3buffer_size - mp3count;
+ /* if user specifed buffer size = 0, dont check size */
+ if (mp3buffer_size == 0)
+ mp3buffer_size_remaining = 0;
+
+ if (gfp->write_id3tag_automatic) {
+ /* write a id3 tag to the bitstream */
+ (void) id3tag_write_v1(gfp);
+
+ imp3 = copy_buffer(gfc, mp3buffer, mp3buffer_size_remaining, 0);
+
+ if (imp3 < 0) {
+ return imp3;
+ }
+ mp3count += imp3;
+ }
+#if 0
+ {
+ int const ed = gfc->ov_enc.encoder_delay;
+ int const ep = gfc->ov_enc.encoder_padding;
+ int const ns = (gfc->ov_enc.frame_number * pcm_samples_per_frame) - (ed + ep);
+ double duration = ns;
+ duration /= cfg->samplerate_out;
+ MSGF(gfc, "frames=%d\n", gfc->ov_enc.frame_number);
+ MSGF(gfc, "pcm_samples_per_frame=%d\n", pcm_samples_per_frame);
+ MSGF(gfc, "encoder delay=%d\n", ed);
+ MSGF(gfc, "encoder padding=%d\n", ep);
+ MSGF(gfc, "sample count=%d (%g)\n", ns, cfg->samplerate_in * duration);
+ MSGF(gfc, "duration=%g sec\n", duration);
+ }
+#endif
+ return mp3count;
+}
+
+/***********************************************************************
+ *
+ * lame_close ()
+ *
+ * frees internal buffers
+ *
+ ***********************************************************************/
+
+int
+lame_close(lame_global_flags * gfp)
+{
+ int ret = 0;
+ if (gfp && gfp->class_id == LAME_ID) {
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ gfp->class_id = 0;
+ if (NULL == gfc || gfc->class_id != LAME_ID) {
+ ret = -3;
+ }
+ if (NULL != gfc) {
+ gfc->class_id = 0;
+ /* this routine will free all malloc'd data in gfc, and then free gfc: */
+ freegfc(gfc);
+ gfp->internal_flags = NULL;
+ }
+ if (gfp->lame_allocated_gfp) {
+ gfp->lame_allocated_gfp = 0;
+ free(gfp);
+ }
+ }
+ return ret;
+}
+
+/*****************************************************************/
+/* flush internal mp3 buffers, and free internal buffers */
+/*****************************************************************/
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+int CDECL
+lame_encode_finish(lame_global_flags * gfp, unsigned char *mp3buffer, int mp3buffer_size);
+#else
+#endif
+
+int
+lame_encode_finish(lame_global_flags * gfp, unsigned char *mp3buffer, int mp3buffer_size)
+{
+ int const ret = lame_encode_flush(gfp, mp3buffer, mp3buffer_size);
+
+ (void) lame_close(gfp);
+
+ return ret;
+}
+
+/*****************************************************************/
+/* write VBR Xing header, and ID3 version 1 tag, if asked for */
+/*****************************************************************/
+void lame_mp3_tags_fid(lame_global_flags * gfp, FILE * fpStream);
+
+void
+lame_mp3_tags_fid(lame_global_flags * gfp, FILE * fpStream)
+{
+ lame_internal_flags *gfc;
+ SessionConfig_t const *cfg;
+ if (!is_lame_global_flags_valid(gfp)) {
+ return;
+ }
+ gfc = gfp->internal_flags;
+ if (!is_lame_internal_flags_valid(gfc)) {
+ return;
+ }
+ cfg = &gfc->cfg;
+ if (!cfg->write_lame_tag) {
+ return;
+ }
+ /* Write Xing header again */
+ if (fpStream && !fseek(fpStream, 0, SEEK_SET)) {
+ int rc = PutVbrTag(gfp, fpStream);
+ switch (rc) {
+ default:
+ /* OK */
+ break;
+
+ case -1:
+ ERRORF(gfc, "Error: could not update LAME tag.\n");
+ break;
+
+ case -2:
+ ERRORF(gfc, "Error: could not update LAME tag, file not seekable.\n");
+ break;
+
+ case -3:
+ ERRORF(gfc, "Error: could not update LAME tag, file not readable.\n");
+ break;
+ }
+ }
+}
+
+
+
+/* initialize mp3 encoder */
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+static
+#else
+#endif
+int
+lame_init_old(lame_global_flags * gfp)
+{
+ lame_internal_flags *gfc;
+ SessionConfig_t *cfg;
+
+ disable_FPE(); /* disable floating point exceptions */
+
+ memset(gfp, 0, sizeof(lame_global_flags));
+
+ gfp->class_id = LAME_ID;
+
+ if (NULL == (gfc = gfp->internal_flags = calloc(1, sizeof(lame_internal_flags))))
+ return -1;
+
+ cfg = &gfc->cfg;
+
+ /* Global flags. set defaults here for non-zero values */
+ /* see lame.h for description */
+ /* set integer values to -1 to mean that LAME will compute the
+ * best value, UNLESS the calling program as set it
+ * (and the value is no longer -1)
+ */
+ gfp->strict_ISO = MDB_MAXIMUM;
+
+ gfp->mode = NOT_SET;
+ gfp->original = 1;
+ gfp->samplerate_in = 44100;
+ gfp->num_channels = 2;
+ gfp->num_samples = MAX_U_32_NUM;
+
+ gfp->write_lame_tag = 1;
+ gfp->quality = -1;
+ gfp->short_blocks = short_block_not_set;
+ gfp->subblock_gain = -1;
+
+ gfp->lowpassfreq = 0;
+ gfp->highpassfreq = 0;
+ gfp->lowpasswidth = -1;
+ gfp->highpasswidth = -1;
+
+ gfp->VBR = vbr_off;
+ gfp->VBR_q = 4;
+ gfp->ATHcurve = -1;
+ gfp->VBR_mean_bitrate_kbps = 128;
+ gfp->VBR_min_bitrate_kbps = 0;
+ gfp->VBR_max_bitrate_kbps = 0;
+ gfp->VBR_hard_min = 0;
+ cfg->vbr_min_bitrate_index = 1; /* not 0 ????? */
+ cfg->vbr_max_bitrate_index = 13; /* not 14 ????? */
+
+ gfp->quant_comp = -1;
+ gfp->quant_comp_short = -1;
+
+ gfp->msfix = -1;
+
+ gfc->sv_qnt.OldValue[0] = 180;
+ gfc->sv_qnt.OldValue[1] = 180;
+ gfc->sv_qnt.CurrentStep[0] = 4;
+ gfc->sv_qnt.CurrentStep[1] = 4;
+ gfc->sv_qnt.masking_lower = 1;
+
+ gfp->attackthre = -1;
+ gfp->attackthre_s = -1;
+
+ gfp->scale = 1;
+ gfp->scale_left = 1;
+ gfp->scale_right = 1;
+
+ gfp->athaa_type = -1;
+ gfp->ATHtype = -1; /* default = -1 = set in lame_init_params */
+ /* 2 = equal loudness curve */
+ gfp->athaa_sensitivity = 0.0; /* no offset */
+ gfp->useTemporal = -1;
+ gfp->interChRatio = -1;
+
+ /* The reason for
+ * int mf_samples_to_encode = ENCDELAY + POSTDELAY;
+ * ENCDELAY = internal encoder delay. And then we have to add POSTDELAY=288
+ * because of the 50% MDCT overlap. A 576 MDCT granule decodes to
+ * 1152 samples. To synthesize the 576 samples centered under this granule
+ * we need the previous granule for the first 288 samples (no problem), and
+ * the next granule for the next 288 samples (not possible if this is last
+ * granule). So we need to pad with 288 samples to make sure we can
+ * encode the 576 samples we are interested in.
+ */
+ gfc->sv_enc.mf_samples_to_encode = ENCDELAY + POSTDELAY;
+ gfc->ov_enc.encoder_padding = 0;
+ gfc->sv_enc.mf_size = ENCDELAY - MDCTDELAY; /* we pad input with this many 0's */
+
+ gfp->findReplayGain = 0;
+ gfp->decode_on_the_fly = 0;
+
+ gfc->cfg.decode_on_the_fly = 0;
+ gfc->cfg.findReplayGain = 0;
+ gfc->cfg.findPeakSample = 0;
+
+ gfc->ov_rpg.RadioGain = 0;
+ gfc->ov_rpg.noclipGainChange = 0;
+ gfc->ov_rpg.noclipScale = -1.0;
+
+ gfp->asm_optimizations.mmx = 1;
+ gfp->asm_optimizations.amd3dnow = 1;
+ gfp->asm_optimizations.sse = 1;
+
+ gfp->preset = 0;
+
+ gfp->write_id3tag_automatic = 1;
+
+ gfp->report.debugf = &lame_report_def;
+ gfp->report.errorf = &lame_report_def;
+ gfp->report.msgf = &lame_report_def;
+ return 0;
+}
+
+
+lame_global_flags *
+lame_init(void)
+{
+ lame_global_flags *gfp;
+ int ret;
+
+ init_log_table();
+
+ gfp = calloc(1, sizeof(lame_global_flags));
+ if (gfp == NULL)
+ return NULL;
+
+ ret = lame_init_old(gfp);
+ if (ret != 0) {
+ free(gfp);
+ return NULL;
+ }
+
+ gfp->lame_allocated_gfp = 1;
+ return gfp;
+}
+
+
+/***********************************************************************
+ *
+ * some simple statistics
+ *
+ * Robert Hegemann 2000-10-11
+ *
+ ***********************************************************************/
+
+/* histogram of used bitrate indexes:
+ * One has to weight them to calculate the average bitrate in kbps
+ *
+ * bitrate indices:
+ * there are 14 possible bitrate indices, 0 has the special meaning
+ * "free format" which is not possible to mix with VBR and 15 is forbidden
+ * anyway.
+ *
+ * stereo modes:
+ * 0: LR number of left-right encoded frames
+ * 1: LR-I number of left-right and intensity encoded frames
+ * 2: MS number of mid-side encoded frames
+ * 3: MS-I number of mid-side and intensity encoded frames
+ *
+ * 4: number of encoded frames
+ *
+ */
+
+void
+lame_bitrate_kbps(const lame_global_flags * gfp, int bitrate_kbps[14])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int i;
+ if (cfg->free_format) {
+ for (i = 0; i < 14; i++)
+ bitrate_kbps[i] = -1;
+ bitrate_kbps[0] = cfg->avg_bitrate;
+ }
+ else {
+ for (i = 0; i < 14; i++)
+ bitrate_kbps[i] = bitrate_table[cfg->version][i + 1];
+ }
+ }
+ }
+}
+
+
+void
+lame_bitrate_hist(const lame_global_flags * gfp, int bitrate_count[14])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int i;
+
+ if (cfg->free_format) {
+ for (i = 0; i < 14; i++) {
+ bitrate_count[i] = 0;
+ }
+ bitrate_count[0] = eov->bitrate_channelmode_hist[0][4];
+ }
+ else {
+ for (i = 0; i < 14; i++) {
+ bitrate_count[i] = eov->bitrate_channelmode_hist[i + 1][4];
+ }
+ }
+ }
+ }
+}
+
+
+void
+lame_stereo_mode_hist(const lame_global_flags * gfp, int stmode_count[4])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ stmode_count[i] = eov->bitrate_channelmode_hist[15][i];
+ }
+ }
+ }
+}
+
+
+
+void
+lame_bitrate_stereo_mode_hist(const lame_global_flags * gfp, int bitrate_stmode_count[14][4])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int i;
+ int j;
+
+ if (cfg->free_format) {
+ for (j = 0; j < 14; j++)
+ for (i = 0; i < 4; i++) {
+ bitrate_stmode_count[j][i] = 0;
+ }
+ for (i = 0; i < 4; i++) {
+ bitrate_stmode_count[0][i] = eov->bitrate_channelmode_hist[0][i];
+ }
+ }
+ else {
+ for (j = 0; j < 14; j++) {
+ for (i = 0; i < 4; i++) {
+ bitrate_stmode_count[j][i] = eov->bitrate_channelmode_hist[j + 1][i];
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void
+lame_block_type_hist(const lame_global_flags * gfp, int btype_count[6])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int i;
+
+ for (i = 0; i < 6; ++i) {
+ btype_count[i] = eov->bitrate_blocktype_hist[15][i];
+ }
+ }
+ }
+}
+
+
+
+void
+lame_bitrate_block_type_hist(const lame_global_flags * gfp, int bitrate_btype_count[14][6])
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t const *const eov = &gfc->ov_enc;
+ int i, j;
+
+ if (cfg->free_format) {
+ for (j = 0; j < 14; ++j) {
+ for (i = 0; i < 6; ++i) {
+ bitrate_btype_count[j][i] = 0;
+ }
+ }
+ for (i = 0; i < 6; ++i) {
+ bitrate_btype_count[0][i] = eov->bitrate_blocktype_hist[0][i];
+ }
+ }
+ else {
+ for (j = 0; j < 14; ++j) {
+ for (i = 0; i < 6; ++i) {
+ bitrate_btype_count[j][i] = eov->bitrate_blocktype_hist[j + 1][i];
+ }
+ }
+ }
+ }
+ }
+}
+
+/* end of lame.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/lame.h b/libnative/src/main/cpp/module/mp3/lame/lame.h
new file mode 100644
index 0000000000000000000000000000000000000000..791d4918c188b6afb201c67dcd2d9b19b4f68a51
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lame.h
@@ -0,0 +1,1323 @@
+/*
+ * Interface to MP3 LAME encoding engine
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */
+
+#ifndef LAME_LAME_H
+#define LAME_LAME_H
+
+/* for size_t typedef */
+#include
+/* for va_list typedef */
+#include
+/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef void (*lame_report_function)(const char *format, va_list ap);
+
+#if defined(WIN32) || defined(_WIN32)
+#undef CDECL
+#define CDECL __cdecl
+#else
+#define CDECL
+#endif
+
+#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1
+
+typedef enum vbr_mode_e {
+ vbr_off=0,
+ vbr_mt, /* obsolete, same as vbr_mtrh */
+ vbr_rh,
+ vbr_abr,
+ vbr_mtrh,
+ vbr_max_indicator, /* Don't use this! It's used for sanity checks. */
+ vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */
+} vbr_mode;
+
+
+/* MPEG modes */
+typedef enum MPEG_mode_e {
+ STEREO = 0,
+ JOINT_STEREO,
+ DUAL_CHANNEL, /* LAME doesn't supports this! */
+ MONO,
+ NOT_SET,
+ MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
+} MPEG_mode;
+
+/* Padding types */
+typedef enum Padding_type_e {
+ PAD_NO = 0,
+ PAD_ALL,
+ PAD_ADJUST,
+ PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
+} Padding_type;
+
+
+
+/*presets*/
+typedef enum preset_mode_e {
+ /*values from 8 to 320 should be reserved for abr bitrates*/
+ /*for abr I'd suggest to directly use the targeted bitrate as a value*/
+ ABR_8 = 8,
+ ABR_320 = 320,
+
+ V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/
+ VBR_10 = 410,
+ V8 = 420,
+ VBR_20 = 420,
+ V7 = 430,
+ VBR_30 = 430,
+ V6 = 440,
+ VBR_40 = 440,
+ V5 = 450,
+ VBR_50 = 450,
+ V4 = 460,
+ VBR_60 = 460,
+ V3 = 470,
+ VBR_70 = 470,
+ V2 = 480,
+ VBR_80 = 480,
+ V1 = 490,
+ VBR_90 = 490,
+ V0 = 500,
+ VBR_100 = 500,
+
+
+
+ /*still there for compatibility*/
+ R3MIX = 1000,
+ STANDARD = 1001,
+ EXTREME = 1002,
+ INSANE = 1003,
+ STANDARD_FAST = 1004,
+ EXTREME_FAST = 1005,
+ MEDIUM = 1006,
+ MEDIUM_FAST = 1007
+} preset_mode;
+
+
+/*asm optimizations*/
+typedef enum asm_optimizations_e {
+ MMX = 1,
+ AMD_3DNOW = 2,
+ SSE = 3
+} asm_optimizations;
+
+
+/* psychoacoustic model */
+typedef enum Psy_model_e {
+ PSY_GPSYCHO = 1,
+ PSY_NSPSYTUNE = 2
+} Psy_model;
+
+
+/* buffer considerations */
+typedef enum buffer_constraint_e {
+ MDB_DEFAULT=0,
+ MDB_STRICT_ISO=1,
+ MDB_MAXIMUM=2
+} buffer_constraint;
+
+
+struct lame_global_struct;
+typedef struct lame_global_struct lame_global_flags;
+typedef lame_global_flags *lame_t;
+
+
+
+
+/***********************************************************************
+ *
+ * The LAME API
+ * These functions should be called, in this order, for each
+ * MP3 file to be encoded. See the file "API" for more documentation
+ *
+ ***********************************************************************/
+
+
+/*
+ * REQUIRED:
+ * initialize the encoder. sets default for all encoder parameters,
+ * returns NULL if some malloc()'s failed
+ * otherwise returns pointer to structure needed for all future
+ * API calls.
+ */
+lame_global_flags * CDECL lame_init(void);
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* obsolete version */
+int CDECL lame_init_old(lame_global_flags *);
+#endif
+
+/*
+ * OPTIONAL:
+ * set as needed to override defaults
+ */
+
+/********************************************************************
+ * input stream description
+ ***********************************************************************/
+/* number of samples. default = 2^32-1 */
+int CDECL lame_set_num_samples(lame_global_flags *, unsigned long);
+unsigned long CDECL lame_get_num_samples(const lame_global_flags *);
+
+/* input sample rate in Hz. default = 44100hz */
+int CDECL lame_set_in_samplerate(lame_global_flags *, int);
+int CDECL lame_get_in_samplerate(const lame_global_flags *);
+
+/* number of channels in input stream. default=2 */
+int CDECL lame_set_num_channels(lame_global_flags *, int);
+int CDECL lame_get_num_channels(const lame_global_flags *);
+
+/*
+ scale the input by this amount before encoding. default=1
+ (not used by decoding routines)
+*/
+int CDECL lame_set_scale(lame_global_flags *, float);
+float CDECL lame_get_scale(const lame_global_flags *);
+
+/*
+ scale the channel 0 (left) input by this amount before encoding. default=1
+ (not used by decoding routines)
+*/
+int CDECL lame_set_scale_left(lame_global_flags *, float);
+float CDECL lame_get_scale_left(const lame_global_flags *);
+
+/*
+ scale the channel 1 (right) input by this amount before encoding. default=1
+ (not used by decoding routines)
+*/
+int CDECL lame_set_scale_right(lame_global_flags *, float);
+float CDECL lame_get_scale_right(const lame_global_flags *);
+
+/*
+ output sample rate in Hz. default = 0, which means LAME picks best value
+ based on the amount of compression. MPEG only allows:
+ MPEG1 32, 44.1, 48khz
+ MPEG2 16, 22.05, 24
+ MPEG2.5 8, 11.025, 12
+ (not used by decoding routines)
+*/
+int CDECL lame_set_out_samplerate(lame_global_flags *, int);
+int CDECL lame_get_out_samplerate(const lame_global_flags *);
+
+
+/********************************************************************
+ * general control parameters
+ ***********************************************************************/
+/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */
+int CDECL lame_set_analysis(lame_global_flags *, int);
+int CDECL lame_get_analysis(const lame_global_flags *);
+
+/*
+ 1 = write a Xing VBR header frame.
+ default = 1
+ this variable must have been added by a Hungarian notation Windows programmer :-)
+*/
+int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int);
+int CDECL lame_get_bWriteVbrTag(const lame_global_flags *);
+
+/* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */
+int CDECL lame_set_decode_only(lame_global_flags *, int);
+int CDECL lame_get_decode_only(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* 1=encode a Vorbis .ogg file. default=0 */
+/* DEPRECATED */
+int CDECL lame_set_ogg(lame_global_flags *, int);
+int CDECL lame_get_ogg(const lame_global_flags *);
+#endif
+
+/*
+ internal algorithm selection. True quality is determined by the bitrate
+ but this variable will effect quality by selecting expensive or cheap algorithms.
+ quality=0..9. 0=best (very slow). 9=worst.
+ recommended: 2 near-best quality, not too slow
+ 5 good quality, fast
+ 7 ok quality, really fast
+*/
+int CDECL lame_set_quality(lame_global_flags *, int);
+int CDECL lame_get_quality(const lame_global_flags *);
+
+/*
+ mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono
+ default: lame picks based on compression ration and input channels
+*/
+int CDECL lame_set_mode(lame_global_flags *, MPEG_mode);
+MPEG_mode CDECL lame_get_mode(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/*
+ mode_automs. Use a M/S mode with a switching threshold based on
+ compression ratio
+ DEPRECATED
+*/
+int CDECL lame_set_mode_automs(lame_global_flags *, int);
+int CDECL lame_get_mode_automs(const lame_global_flags *);
+#endif
+
+/*
+ force_ms. Force M/S for all frames. For testing only.
+ default = 0 (disabled)
+*/
+int CDECL lame_set_force_ms(lame_global_flags *, int);
+int CDECL lame_get_force_ms(const lame_global_flags *);
+
+/* use free_format? default = 0 (disabled) */
+int CDECL lame_set_free_format(lame_global_flags *, int);
+int CDECL lame_get_free_format(const lame_global_flags *);
+
+/* perform ReplayGain analysis? default = 0 (disabled) */
+int CDECL lame_set_findReplayGain(lame_global_flags *, int);
+int CDECL lame_get_findReplayGain(const lame_global_flags *);
+
+/* decode on the fly. Search for the peak sample. If the ReplayGain
+ * analysis is enabled then perform the analysis on the decoded data
+ * stream. default = 0 (disabled)
+ * NOTE: if this option is set the build-in decoder should not be used */
+int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int);
+int CDECL lame_get_decode_on_the_fly(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* DEPRECATED: now does the same as lame_set_findReplayGain()
+ default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_input(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_input(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as
+ lame_set_decode_on_the_fly() && lame_set_findReplayGain()
+ default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_decode(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as lame_set_decode_on_the_fly()
+ default = 0 (disabled) */
+int CDECL lame_set_findPeakSample(lame_global_flags *, int);
+int CDECL lame_get_findPeakSample(const lame_global_flags *);
+#endif
+
+/* counters for gapless encoding */
+int CDECL lame_set_nogap_total(lame_global_flags*, int);
+int CDECL lame_get_nogap_total(const lame_global_flags*);
+
+int CDECL lame_set_nogap_currentindex(lame_global_flags* , int);
+int CDECL lame_get_nogap_currentindex(const lame_global_flags*);
+
+
+/*
+ * OPTIONAL:
+ * Set printf like error/debug/message reporting functions.
+ * The second argument has to be a pointer to a function which looks like
+ * void my_debugf(const char *format, va_list ap)
+ * {
+ * (void) vfprintf(stdout, format, ap);
+ * }
+ * If you use NULL as the value of the pointer in the set function, the
+ * lame buildin function will be used (prints to stderr).
+ * To quiet any output you have to replace the body of the example function
+ * with just "return;" and use it in the set function.
+ */
+int CDECL lame_set_errorf(lame_global_flags *, lame_report_function);
+int CDECL lame_set_debugf(lame_global_flags *, lame_report_function);
+int CDECL lame_set_msgf (lame_global_flags *, lame_report_function);
+
+
+
+/* set one of brate compression ratio. default is compression ratio of 11. */
+int CDECL lame_set_brate(lame_global_flags *, int);
+int CDECL lame_get_brate(const lame_global_flags *);
+int CDECL lame_set_compression_ratio(lame_global_flags *, float);
+float CDECL lame_get_compression_ratio(const lame_global_flags *);
+
+
+int CDECL lame_set_preset( lame_global_flags* gfp, int );
+int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int );
+
+
+
+/********************************************************************
+ * frame params
+ ***********************************************************************/
+/* mark as copyright. default=0 */
+int CDECL lame_set_copyright(lame_global_flags *, int);
+int CDECL lame_get_copyright(const lame_global_flags *);
+
+/* mark as original. default=1 */
+int CDECL lame_set_original(lame_global_flags *, int);
+int CDECL lame_get_original(const lame_global_flags *);
+
+/* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */
+int CDECL lame_set_error_protection(lame_global_flags *, int);
+int CDECL lame_get_error_protection(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */
+int CDECL lame_set_padding_type(lame_global_flags *, Padding_type);
+Padding_type CDECL lame_get_padding_type(const lame_global_flags *);
+#endif
+
+/* MP3 'private extension' bit Meaningless. default=0 */
+int CDECL lame_set_extension(lame_global_flags *, int);
+int CDECL lame_get_extension(const lame_global_flags *);
+
+/* enforce strict ISO compliance. default=0 */
+int CDECL lame_set_strict_ISO(lame_global_flags *, int);
+int CDECL lame_get_strict_ISO(const lame_global_flags *);
+
+
+/********************************************************************
+ * quantization/noise shaping
+ ***********************************************************************/
+
+/* disable the bit reservoir. For testing only. default=0 */
+int CDECL lame_set_disable_reservoir(lame_global_flags *, int);
+int CDECL lame_get_disable_reservoir(const lame_global_flags *);
+
+/* select a different "best quantization" function. default=0 */
+int CDECL lame_set_quant_comp(lame_global_flags *, int);
+int CDECL lame_get_quant_comp(const lame_global_flags *);
+int CDECL lame_set_quant_comp_short(lame_global_flags *, int);
+int CDECL lame_get_quant_comp_short(const lame_global_flags *);
+
+int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/
+int CDECL lame_get_experimentalX(const lame_global_flags *);
+
+/* another experimental option. for testing only */
+int CDECL lame_set_experimentalY(lame_global_flags *, int);
+int CDECL lame_get_experimentalY(const lame_global_flags *);
+
+/* another experimental option. for testing only */
+int CDECL lame_set_experimentalZ(lame_global_flags *, int);
+int CDECL lame_get_experimentalZ(const lame_global_flags *);
+
+/* Naoki's psycho acoustic model. default=0 */
+int CDECL lame_set_exp_nspsytune(lame_global_flags *, int);
+int CDECL lame_get_exp_nspsytune(const lame_global_flags *);
+
+void CDECL lame_set_msfix(lame_global_flags *, double);
+float CDECL lame_get_msfix(const lame_global_flags *);
+
+
+/********************************************************************
+ * VBR control
+ ***********************************************************************/
+/* Types of VBR. default = vbr_off = CBR */
+int CDECL lame_set_VBR(lame_global_flags *, vbr_mode);
+vbr_mode CDECL lame_get_VBR(const lame_global_flags *);
+
+/* VBR quality level. 0=highest 9=lowest */
+int CDECL lame_set_VBR_q(lame_global_flags *, int);
+int CDECL lame_get_VBR_q(const lame_global_flags *);
+
+/* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */
+int CDECL lame_set_VBR_quality(lame_global_flags *, float);
+float CDECL lame_get_VBR_quality(const lame_global_flags *);
+
+/* Ignored except for VBR=vbr_abr (ABR mode) */
+int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *);
+
+int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *);
+
+int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *);
+
+/*
+ 1=strictly enforce VBR_min_bitrate. Normally it will be violated for
+ analog silence
+*/
+int CDECL lame_set_VBR_hard_min(lame_global_flags *, int);
+int CDECL lame_get_VBR_hard_min(const lame_global_flags *);
+
+/* for preset */
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+int CDECL lame_set_preset_expopts(lame_global_flags *, int);
+#endif
+
+/********************************************************************
+ * Filtering control
+ ***********************************************************************/
+/* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */
+int CDECL lame_set_lowpassfreq(lame_global_flags *, int);
+int CDECL lame_get_lowpassfreq(const lame_global_flags *);
+/* width of transition band, in Hz. Default = one polyphase filter band */
+int CDECL lame_set_lowpasswidth(lame_global_flags *, int);
+int CDECL lame_get_lowpasswidth(const lame_global_flags *);
+
+/* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */
+int CDECL lame_set_highpassfreq(lame_global_flags *, int);
+int CDECL lame_get_highpassfreq(const lame_global_flags *);
+/* width of transition band, in Hz. Default = one polyphase filter band */
+int CDECL lame_set_highpasswidth(lame_global_flags *, int);
+int CDECL lame_get_highpasswidth(const lame_global_flags *);
+
+
+/********************************************************************
+ * psycho acoustics and other arguments which you should not change
+ * unless you know what you are doing
+ ***********************************************************************/
+
+/* only use ATH for masking */
+int CDECL lame_set_ATHonly(lame_global_flags *, int);
+int CDECL lame_get_ATHonly(const lame_global_flags *);
+
+/* only use ATH for short blocks */
+int CDECL lame_set_ATHshort(lame_global_flags *, int);
+int CDECL lame_get_ATHshort(const lame_global_flags *);
+
+/* disable ATH */
+int CDECL lame_set_noATH(lame_global_flags *, int);
+int CDECL lame_get_noATH(const lame_global_flags *);
+
+/* select ATH formula */
+int CDECL lame_set_ATHtype(lame_global_flags *, int);
+int CDECL lame_get_ATHtype(const lame_global_flags *);
+
+/* lower ATH by this many db */
+int CDECL lame_set_ATHlower(lame_global_flags *, float);
+float CDECL lame_get_ATHlower(const lame_global_flags *);
+
+/* select ATH adaptive adjustment type */
+int CDECL lame_set_athaa_type( lame_global_flags *, int);
+int CDECL lame_get_athaa_type( const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* select the loudness approximation used by the ATH adaptive auto-leveling */
+int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int);
+int CDECL lame_get_athaa_loudapprox( const lame_global_flags *);
+#endif
+
+/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */
+int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float);
+float CDECL lame_get_athaa_sensitivity( const lame_global_flags* );
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* OBSOLETE: predictability limit (ISO tonality formula) */
+int CDECL lame_set_cwlimit(lame_global_flags *, int);
+int CDECL lame_get_cwlimit(const lame_global_flags *);
+#endif
+
+/*
+ allow blocktypes to differ between channels?
+ default: 0 for jstereo, 1 for stereo
+*/
+int CDECL lame_set_allow_diff_short(lame_global_flags *, int);
+int CDECL lame_get_allow_diff_short(const lame_global_flags *);
+
+/* use temporal masking effect (default = 1) */
+int CDECL lame_set_useTemporal(lame_global_flags *, int);
+int CDECL lame_get_useTemporal(const lame_global_flags *);
+
+/* use temporal masking effect (default = 1) */
+int CDECL lame_set_interChRatio(lame_global_flags *, float);
+float CDECL lame_get_interChRatio(const lame_global_flags *);
+
+/* disable short blocks */
+int CDECL lame_set_no_short_blocks(lame_global_flags *, int);
+int CDECL lame_get_no_short_blocks(const lame_global_flags *);
+
+/* force short blocks */
+int CDECL lame_set_force_short_blocks(lame_global_flags *, int);
+int CDECL lame_get_force_short_blocks(const lame_global_flags *);
+
+/* Input PCM is emphased PCM (for instance from one of the rarely
+ emphased CDs), it is STRONGLY not recommended to use this, because
+ psycho does not take it into account, and last but not least many decoders
+ ignore these bits */
+int CDECL lame_set_emphasis(lame_global_flags *, int);
+int CDECL lame_get_emphasis(const lame_global_flags *);
+
+
+
+/************************************************************************/
+/* internal variables, cannot be set... */
+/* provided because they may be of use to calling application */
+/************************************************************************/
+/* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */
+int CDECL lame_get_version(const lame_global_flags *);
+
+/* encoder delay */
+int CDECL lame_get_encoder_delay(const lame_global_flags *);
+
+/*
+ padding appended to the input to make sure decoder can fully decode
+ all input. Note that this value can only be calculated during the
+ call to lame_encoder_flush(). Before lame_encoder_flush() has
+ been called, the value of encoder_padding = 0.
+*/
+int CDECL lame_get_encoder_padding(const lame_global_flags *);
+
+/* size of MPEG frame */
+int CDECL lame_get_framesize(const lame_global_flags *);
+
+/* number of PCM samples buffered, but not yet encoded to mp3 data. */
+int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp );
+
+/*
+ size (bytes) of mp3 data buffered, but not yet encoded.
+ this is the number of bytes which would be output by a call to
+ lame_encode_flush_nogap. NOTE: lame_encode_flush() will return
+ more bytes than this because it will encode the reamining buffered
+ PCM samples before flushing the mp3 buffers.
+*/
+int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp );
+
+/* number of frames encoded so far */
+int CDECL lame_get_frameNum(const lame_global_flags *);
+
+/*
+ lame's estimate of the total number of frames to be encoded
+ only valid if calling program set num_samples
+*/
+int CDECL lame_get_totalframes(const lame_global_flags *);
+
+/* RadioGain value. Multiplied by 10 and rounded to the nearest. */
+int CDECL lame_get_RadioGain(const lame_global_flags *);
+
+/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */
+int CDECL lame_get_AudiophileGain(const lame_global_flags *);
+
+/* the peak sample */
+float CDECL lame_get_PeakSample(const lame_global_flags *);
+
+/* Gain change required for preventing clipping. The value is correct only if
+ peak sample searching was enabled. If negative then the waveform
+ already does not clip. The value is multiplied by 10 and rounded up. */
+int CDECL lame_get_noclipGainChange(const lame_global_flags *);
+
+/* user-specified scale factor required for preventing clipping. Value is
+ correct only if peak sample searching was enabled and no user-specified
+ scaling was performed. If negative then either the waveform already does
+ not clip or the value cannot be determined */
+float CDECL lame_get_noclipScale(const lame_global_flags *);
+
+
+
+
+
+
+
+/*
+ * REQUIRED:
+ * sets more internal configuration based on data provided above.
+ * returns -1 if something failed.
+ */
+int CDECL lame_init_params(lame_global_flags *);
+
+
+/*
+ * OPTIONAL:
+ * get the version number, in a string. of the form:
+ * "3.63 (beta)" or just "3.63".
+ */
+const char* CDECL get_lame_version ( void );
+const char* CDECL get_lame_short_version ( void );
+const char* CDECL get_lame_very_short_version ( void );
+const char* CDECL get_psy_version ( void );
+const char* CDECL get_lame_url ( void );
+const char* CDECL get_lame_os_bitness ( void );
+
+/*
+ * OPTIONAL:
+ * get the version numbers in numerical form.
+ */
+typedef struct {
+ /* generic LAME version */
+ int major;
+ int minor;
+ int alpha; /* 0 if not an alpha version */
+ int beta; /* 0 if not a beta version */
+
+ /* version of the psy model */
+ int psy_major;
+ int psy_minor;
+ int psy_alpha; /* 0 if not an alpha version */
+ int psy_beta; /* 0 if not a beta version */
+
+ /* compile time features */
+ const char *features; /* Don't make assumptions about the contents! */
+} lame_version_t;
+void CDECL get_lame_version_numerical(lame_version_t *);
+
+
+/*
+ * OPTIONAL:
+ * print internal lame configuration to message handler
+ */
+void CDECL lame_print_config(const lame_global_flags* gfp);
+
+void CDECL lame_print_internals( const lame_global_flags *gfp);
+
+
+/*
+ * input pcm data, output (maybe) mp3 frames.
+ * This routine handles all buffering, resampling and filtering for you.
+ *
+ * return code number of bytes output in mp3buf. Can be 0
+ * -1: mp3buf was too small
+ * -2: malloc() problem
+ * -3: lame_init_params() not called
+ * -4: psycho acoustic problems
+ *
+ * The required mp3buf_size can be computed from num_samples,
+ * samplerate and encoding rate, but here is a worst case estimate:
+ *
+ * mp3buf_size in bytes = 1.25*num_samples + 7200
+ *
+ * I think a tighter bound could be: (mt, March 2000)
+ * MPEG1:
+ * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
+ * MPEG2:
+ * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
+ *
+ * but test first if you use that!
+ *
+ * set mp3buf_size = 0 and LAME will not check if mp3buf_size is
+ * large enough.
+ *
+ * NOTE:
+ * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels
+ * will be averaged into the L channel before encoding only the L channel
+ * This will overwrite the data in buffer_l[] and buffer_r[].
+ *
+*/
+int CDECL lame_encode_buffer (
+ lame_global_flags* gfp, /* global context handle */
+ const short int buffer_l [], /* PCM data for left channel */
+ const short int buffer_r [], /* PCM data for right channel */
+ const int nsamples, /* number of samples per channel */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ const int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+/*
+ * as above, but input has L & R channel data interleaved.
+ * NOTE:
+ * num_samples = number of samples in the L (or R)
+ * channel, not the total number of samples in pcm[]
+ */
+int CDECL lame_encode_buffer_interleaved(
+ lame_global_flags* gfp, /* global context handlei */
+ short int pcm[], /* PCM data for left and right
+ channel, interleaved */
+ int num_samples, /* number of samples per channel,
+ _not_ number of samples in
+ pcm[] */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+
+/* as lame_encode_buffer, but for 'float's.
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * short int, +/- 32768
+ */
+int CDECL lame_encode_buffer_float(
+ lame_global_flags* gfp, /* global context handle */
+ const float pcm_l [], /* PCM data for left channel */
+ const float pcm_r [], /* PCM data for right channel */
+ const int nsamples, /* number of samples per channel */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ const int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+/* as lame_encode_buffer, but for 'float's.
+ * !! NOTE: !! data must be scaled to +/- 1 full scale
+ */
+int CDECL lame_encode_buffer_ieee_float(
+ lame_t gfp,
+ const float pcm_l [], /* PCM data for left channel */
+ const float pcm_r [], /* PCM data for right channel */
+ const int nsamples,
+ unsigned char * mp3buf,
+ const int mp3buf_size);
+int CDECL lame_encode_buffer_interleaved_ieee_float(
+ lame_t gfp,
+ const float pcm[], /* PCM data for left and right
+ channel, interleaved */
+ const int nsamples,
+ unsigned char * mp3buf,
+ const int mp3buf_size);
+
+/* as lame_encode_buffer, but for 'double's.
+ * !! NOTE: !! data must be scaled to +/- 1 full scale
+ */
+int CDECL lame_encode_buffer_ieee_double(
+ lame_t gfp,
+ const double pcm_l [], /* PCM data for left channel */
+ const double pcm_r [], /* PCM data for right channel */
+ const int nsamples,
+ unsigned char * mp3buf,
+ const int mp3buf_size);
+int CDECL lame_encode_buffer_interleaved_ieee_double(
+ lame_t gfp,
+ const double pcm[], /* PCM data for left and right
+ channel, interleaved */
+ const int nsamples,
+ unsigned char * mp3buf,
+ const int mp3buf_size);
+
+/* as lame_encode_buffer, but for long's
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * short int, +/- 32768
+ *
+ * This scaling was a mistake (doesn't allow one to exploit full
+ * precision of type 'long'. Use lame_encode_buffer_long2() instead.
+ *
+ */
+int CDECL lame_encode_buffer_long(
+ lame_global_flags* gfp, /* global context handle */
+ const long buffer_l [], /* PCM data for left channel */
+ const long buffer_r [], /* PCM data for right channel */
+ const int nsamples, /* number of samples per channel */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ const int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+/* Same as lame_encode_buffer_long(), but with correct scaling.
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1)
+ *
+ */
+int CDECL lame_encode_buffer_long2(
+ lame_global_flags* gfp, /* global context handle */
+ const long buffer_l [], /* PCM data for left channel */
+ const long buffer_r [], /* PCM data for right channel */
+ const int nsamples, /* number of samples per channel */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ const int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+/* as lame_encode_buffer, but for int's
+ * !! NOTE: !! input should be scaled to the maximum range of 'int'
+ * If int is 4 bytes, then the values should range from
+ * +/- 2147483648.
+ *
+ * This routine does not (and cannot, without loosing precision) use
+ * the same scaling as the rest of the lame_encode_buffer() routines.
+ *
+ */
+int CDECL lame_encode_buffer_int(
+ lame_global_flags* gfp, /* global context handle */
+ const int buffer_l [], /* PCM data for left channel */
+ const int buffer_r [], /* PCM data for right channel */
+ const int nsamples, /* number of samples per channel */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ const int mp3buf_size ); /* number of valid octets in this
+ stream */
+
+
+
+
+
+/*
+ * REQUIRED:
+ * lame_encode_flush will flush the intenal PCM buffers, padding with
+ * 0's to make sure the final frame is complete, and then flush
+ * the internal MP3 buffers, and thus may return a
+ * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long
+ * to hold all possible emitted data.
+ *
+ * will also write id3v1 tags (if any) into the bitstream
+ *
+ * return code = number of bytes output to mp3buf. Can be 0
+ */
+int CDECL lame_encode_flush(
+ lame_global_flags * gfp, /* global context handle */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ int size); /* number of valid octets in this stream */
+
+/*
+ * OPTIONAL:
+ * lame_encode_flush_nogap will flush the internal mp3 buffers and pad
+ * the last frame with ancillary data so it is a complete mp3 frame.
+ *
+ * 'mp3buf' should be at least 7200 bytes long
+ * to hold all possible emitted data.
+ *
+ * After a call to this routine, the outputed mp3 data is complete, but
+ * you may continue to encode new PCM samples and write future mp3 data
+ * to a different file. The two mp3 files will play back with no gaps
+ * if they are concatenated together.
+ *
+ * This routine will NOT write id3v1 tags into the bitstream.
+ *
+ * return code = number of bytes output to mp3buf. Can be 0
+ */
+int CDECL lame_encode_flush_nogap(
+ lame_global_flags * gfp, /* global context handle */
+ unsigned char* mp3buf, /* pointer to encoded MP3 stream */
+ int size); /* number of valid octets in this stream */
+
+/*
+ * OPTIONAL:
+ * Normally, this is called by lame_init_params(). It writes id3v2 and
+ * Xing headers into the front of the bitstream, and sets frame counters
+ * and bitrate histogram data to 0. You can also call this after
+ * lame_encode_flush_nogap().
+ */
+int CDECL lame_init_bitstream(
+ lame_global_flags * gfp); /* global context handle */
+
+
+
+/*
+ * OPTIONAL: some simple statistics
+ * a bitrate histogram to visualize the distribution of used frame sizes
+ * a stereo mode histogram to visualize the distribution of used stereo
+ * modes, useful in joint-stereo mode only
+ * 0: LR left-right encoded
+ * 1: LR-I left-right and intensity encoded (currently not supported)
+ * 2: MS mid-side encoded
+ * 3: MS-I mid-side and intensity encoded (currently not supported)
+ *
+ * attention: don't call them after lame_encode_finish
+ * suggested: lame_encode_flush -> lame_*_hist -> lame_close
+ */
+
+void CDECL lame_bitrate_hist(
+ const lame_global_flags * gfp,
+ int bitrate_count[14] );
+void CDECL lame_bitrate_kbps(
+ const lame_global_flags * gfp,
+ int bitrate_kbps [14] );
+void CDECL lame_stereo_mode_hist(
+ const lame_global_flags * gfp,
+ int stereo_mode_count[4] );
+
+void CDECL lame_bitrate_stereo_mode_hist (
+ const lame_global_flags * gfp,
+ int bitrate_stmode_count[14][4] );
+
+void CDECL lame_block_type_hist (
+ const lame_global_flags * gfp,
+ int btype_count[6] );
+
+void CDECL lame_bitrate_block_type_hist (
+ const lame_global_flags * gfp,
+ int bitrate_btype_count[14][6] );
+
+#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0)
+#else
+/*
+ * OPTIONAL:
+ * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file
+ * pointer fid. These calls perform forward and backwards seeks, so make
+ * sure fid is a real file. Make sure lame_encode_flush has been called,
+ * and all mp3 data has been written to the file before calling this
+ * function.
+ * NOTE:
+ * if VBR tags are turned off by the user, or turned off by LAME because
+ * the output is not a regular file, this call does nothing
+ * NOTE:
+ * LAME wants to read from the file to skip an optional ID3v2 tag, so
+ * make sure you opened the file for writing and reading.
+ * NOTE:
+ * You can call lame_get_lametag_frame instead, if you want to insert
+ * the lametag yourself.
+*/
+void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid);
+#endif
+
+/*
+ * OPTIONAL:
+ * lame_get_lametag_frame copies the final LAME-tag into 'buffer'.
+ * The function returns the number of bytes copied into buffer, or
+ * the required buffer size, if the provided buffer is too small.
+ * Function failed, if the return value is larger than 'size'!
+ * Make sure lame_encode flush has been called before calling this function.
+ * NOTE:
+ * if VBR tags are turned off by the user, or turned off by LAME,
+ * this call does nothing and returns 0.
+ * NOTE:
+ * LAME inserted an empty frame in the beginning of mp3 audio data,
+ * which you have to replace by the final LAME-tag frame after encoding.
+ * In case there is no ID3v2 tag, usually this frame will be the very first
+ * data in your mp3 file. If you put some other leading data into your
+ * file, you'll have to do some bookkeeping about where to write this buffer.
+ */
+size_t CDECL lame_get_lametag_frame(
+ const lame_global_flags *, unsigned char* buffer, size_t size);
+
+/*
+ * REQUIRED:
+ * final call to free all remaining buffers
+ */
+int CDECL lame_close (lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/*
+ * OBSOLETE:
+ * lame_encode_finish combines lame_encode_flush() and lame_close() in
+ * one call. However, once this call is made, the statistics routines
+ * will no longer work because the data will have been cleared, and
+ * lame_mp3_tags_fid() cannot be called to add data to the VBR header
+ */
+int CDECL lame_encode_finish(
+ lame_global_flags* gfp,
+ unsigned char* mp3buf,
+ int size );
+#endif
+
+
+
+
+
+
+/*********************************************************************
+ *
+ * decoding
+ *
+ * a simple interface to mpglib, part of mpg123, is also included if
+ * libmp3lame is compiled with HAVE_MPGLIB
+ *
+ *********************************************************************/
+
+struct hip_global_struct;
+typedef struct hip_global_struct hip_global_flags;
+typedef hip_global_flags *hip_t;
+
+
+typedef struct {
+ int header_parsed; /* 1 if header was parsed and following data was
+ computed */
+ int stereo; /* number of channels */
+ int samplerate; /* sample rate */
+ int bitrate; /* bitrate */
+ int mode; /* mp3 frame type */
+ int mode_ext; /* mp3 frame type */
+ int framesize; /* number of samples per mp3 frame */
+
+ /* this data is only computed if mpglib detects a Xing VBR header */
+ unsigned long nsamp; /* number of samples in mp3 file. */
+ int totalframes; /* total number of frames in mp3 file */
+
+ /* this data is not currently computed by the mpglib routines */
+ int framenum; /* frames decoded counter */
+} mp3data_struct;
+
+/* required call to initialize decoder */
+hip_t CDECL hip_decode_init(void);
+
+/* cleanup call to exit decoder */
+int CDECL hip_decode_exit(hip_t gfp);
+
+/* HIP reporting functions */
+void CDECL hip_set_errorf(hip_t gfp, lame_report_function f);
+void CDECL hip_set_debugf(hip_t gfp, lame_report_function f);
+void CDECL hip_set_msgf (hip_t gfp, lame_report_function f);
+
+/*********************************************************************
+ * input 1 mp3 frame, output (maybe) pcm data.
+ *
+ * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r);
+ *
+ * input:
+ * len : number of bytes of mp3 data in mp3buf
+ * mp3buf[len] : mp3 data to be decoded
+ *
+ * output:
+ * nout: -1 : decoding error
+ * 0 : need more data before we can complete the decode
+ * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r
+ * pcm_l[nout] : left channel data
+ * pcm_r[nout] : right channel data
+ *
+ *********************************************************************/
+int CDECL hip_decode( hip_t gfp
+ , unsigned char * mp3buf
+ , size_t len
+ , short pcm_l[]
+ , short pcm_r[]
+ );
+
+/* same as hip_decode, and also returns mp3 header data */
+int CDECL hip_decode_headers( hip_t gfp
+ , unsigned char* mp3buf
+ , size_t len
+ , short pcm_l[]
+ , short pcm_r[]
+ , mp3data_struct* mp3data
+ );
+
+/* same as hip_decode, but returns at most one frame */
+int CDECL hip_decode1( hip_t gfp
+ , unsigned char* mp3buf
+ , size_t len
+ , short pcm_l[]
+ , short pcm_r[]
+ );
+
+/* same as hip_decode1, but returns at most one frame and mp3 header data */
+int CDECL hip_decode1_headers( hip_t gfp
+ , unsigned char* mp3buf
+ , size_t len
+ , short pcm_l[]
+ , short pcm_r[]
+ , mp3data_struct* mp3data
+ );
+
+/* same as hip_decode1_headers, but also returns enc_delay and enc_padding
+ from VBR Info tag, (-1 if no info tag was found) */
+int CDECL hip_decode1_headersB( hip_t gfp
+ , unsigned char* mp3buf
+ , size_t len
+ , short pcm_l[]
+ , short pcm_r[]
+ , mp3data_struct* mp3data
+ , int *enc_delay
+ , int *enc_padding
+ );
+
+
+
+/* OBSOLETE:
+ * lame_decode... functions are there to keep old code working
+ * but it is strongly recommended to replace calls by hip_decode...
+ * function calls, see above.
+ */
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+int CDECL lame_decode_init(void);
+int CDECL lame_decode(
+ unsigned char * mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[] );
+int CDECL lame_decode_headers(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data );
+int CDECL lame_decode1(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[] );
+int CDECL lame_decode1_headers(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data );
+int CDECL lame_decode1_headersB(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data,
+ int *enc_delay,
+ int *enc_padding );
+int CDECL lame_decode_exit(void);
+
+#endif /* obsolete lame_decode API calls */
+
+
+/*********************************************************************
+ *
+ * id3tag stuff
+ *
+ *********************************************************************/
+
+/*
+ * id3tag.h -- Interface to write ID3 version 1 and 2 tags.
+ *
+ * Copyright (C) 2000 Don Melton.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* utility to obtain alphabetically sorted list of genre names with numbers */
+void CDECL id3tag_genre_list(
+ void (*handler)(int, const char *, void *),
+ void* cookie);
+
+void CDECL id3tag_init (lame_t gfp);
+
+/* force addition of version 2 tag */
+void CDECL id3tag_add_v2 (lame_t gfp);
+
+/* add only a version 1 tag */
+void CDECL id3tag_v1_only (lame_t gfp);
+
+/* add only a version 2 tag */
+void CDECL id3tag_v2_only (lame_t gfp);
+
+/* pad version 1 tag with spaces instead of nulls */
+void CDECL id3tag_space_v1 (lame_t gfp);
+
+/* pad version 2 tag with extra 128 bytes */
+void CDECL id3tag_pad_v2 (lame_t gfp);
+
+/* pad version 2 tag with extra n bytes */
+void CDECL id3tag_set_pad (lame_t gfp, size_t n);
+
+void CDECL id3tag_set_title(lame_t gfp, const char* title);
+void CDECL id3tag_set_artist(lame_t gfp, const char* artist);
+void CDECL id3tag_set_album(lame_t gfp, const char* album);
+void CDECL id3tag_set_year(lame_t gfp, const char* year);
+void CDECL id3tag_set_comment(lame_t gfp, const char* comment);
+
+/* return -1 result if track number is out of ID3v1 range
+ and ignored for ID3v1 */
+int CDECL id3tag_set_track(lame_t gfp, const char* track);
+
+/* return non-zero result if genre name or number is invalid
+ result 0: OK
+ result -1: genre number out of range
+ result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other'
+ but taken as-is for ID3v2 genre tag */
+int CDECL id3tag_set_genre(lame_t gfp, const char* genre);
+
+/* return non-zero result if field name is invalid */
+int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue);
+
+/* return non-zero result if image type is invalid */
+int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size);
+
+/* lame_get_id3v1_tag copies ID3v1 tag into buffer.
+ * Function returns number of bytes copied into buffer, or number
+ * of bytes rquired if buffer 'size' is too small.
+ * Function fails, if returned value is larger than 'size'.
+ * NOTE:
+ * This functions does nothing, if user/LAME disabled ID3v1 tag.
+ */
+size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size);
+
+/* lame_get_id3v2_tag copies ID3v2 tag into buffer.
+ * Function returns number of bytes copied into buffer, or number
+ * of bytes rquired if buffer 'size' is too small.
+ * Function fails, if returned value is larger than 'size'.
+ * NOTE:
+ * This functions does nothing, if user/LAME disabled ID3v2 tag.
+ */
+size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size);
+
+/* normaly lame_init_param writes ID3v2 tags into the audio stream
+ * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param
+ * to turn off this behaviour and get ID3v2 tag with above function
+ * write it yourself into your file.
+ */
+void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int);
+int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp);
+
+/* experimental */
+int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* experimental */
+int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang,
+ unsigned short const *desc, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue);
+#endif
+
+/* experimental */
+int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue);
+
+/* experimental */
+int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text);
+
+
+/***********************************************************************
+*
+* list of valid bitrates [kbps] & sample frequencies [Hz].
+* first index: 0: MPEG-2 values (sample frequencies 16...24 kHz)
+* 1: MPEG-1 values (sample frequencies 32...48 kHz)
+* 2: MPEG-2.5 values (sample frequencies 8...12 kHz)
+***********************************************************************/
+
+extern const int bitrate_table [3][16];
+extern const int samplerate_table [3][ 4];
+
+/* access functions for use in DLL, global vars are not exported */
+int CDECL lame_get_bitrate(int mpeg_version, int table_index);
+int CDECL lame_get_samplerate(int mpeg_version, int table_index);
+
+
+/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER
+ as well since lame_encode_buffer() also returns ID3v2 tag data */
+#define LAME_MAXALBUMART (128 * 1024)
+
+/* maximum size of mp3buffer needed if you encode at most 1152 samples for
+ each call to lame_encode_buffer. see lame_encode_buffer() below
+ (LAME_MAXMP3BUFFER is now obsolete) */
+#define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART)
+
+
+typedef enum {
+ LAME_OKAY = 0,
+ LAME_NOERROR = 0,
+ LAME_GENERICERROR = -1,
+ LAME_NOMEM = -10,
+ LAME_BADBITRATE = -11,
+ LAME_BADSAMPFREQ = -12,
+ LAME_INTERNALERROR = -13,
+
+ FRONTEND_READERROR = -80,
+ FRONTEND_WRITEERROR = -81,
+ FRONTEND_FILETOOLARGE = -82
+
+} lame_errorcodes_t;
+
+#if defined(__cplusplus)
+}
+#endif
+#endif /* LAME_LAME_H */
+
diff --git a/libnative/src/main/cpp/module/mp3/lame/lame.rc b/libnative/src/main/cpp/module/mp3/lame/lame.rc
new file mode 100644
index 0000000000000000000000000000000000000000..12d0193e277e9d91ccf487d61283ec2230e7feff
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lame.rc
@@ -0,0 +1,50 @@
+#include
+#include "version.h"
+
+#ifdef _DLL
+IDI_ICON1 ICON DISCARDABLE "logoe.ico"
+#else
+IDI_ICON1 ICON DISCARDABLE "logoe.ico"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION LAME_MAJOR_VERSION,LAME_MINOR_VERSION,LAME_TYPE_VERSION,LAME_PATCH_VERSION
+ PRODUCTVERSION LAME_MAJOR_VERSION,LAME_MINOR_VERSION,LAME_TYPE_VERSION,LAME_PATCH_VERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+#ifdef _DLL
+ FILETYPE VFT_DLL
+#else
+ FILETYPE VFT_APP
+#endif
+ FILESUBTYPE 0x0L
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
+ BEGIN
+ VALUE "CompanyName", LAME_URL "\0"
+ VALUE "FileDescription", "MP3 Encoder.\0"
+ VALUE "FileVersion", LAME_VERSION_STRING "\0"
+ VALUE "LegalCopyright", "Copyright (C) 1999-2011 The L.A.M.E. Team\0"
+#ifdef _DLL
+ VALUE "OriginalFilename", STR(_DLL) "\0"
+#else
+ VALUE "OriginalFilename", STR(_APP) "\0"
+#endif
+ VALUE "ProductName", "L.A.M.E.\0"
+ VALUE "ProductVersion", LAME_VERSION_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252 // mandatory by convention
+ END
+ END
+/* End of Version info */
+
diff --git a/libnative/src/main/cpp/module/mp3/lame/lame_global_flags.h b/libnative/src/main/cpp/module/mp3/lame/lame_global_flags.h
new file mode 100644
index 0000000000000000000000000000000000000000..ad9e677da0a585251bc6a2a3bea2ad85e37272c5
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lame_global_flags.h
@@ -0,0 +1,184 @@
+#ifndef LAME_GLOBAL_FLAGS_H
+#define LAME_GLOBAL_FLAGS_H
+
+#ifndef lame_internal_flags_defined
+#define lame_internal_flags_defined
+struct lame_internal_flags;
+typedef struct lame_internal_flags lame_internal_flags;
+#endif
+
+
+typedef enum short_block_e {
+ short_block_not_set = -1, /* allow LAME to decide */
+ short_block_allowed = 0, /* LAME may use them, even different block types for L/R */
+ short_block_coupled, /* LAME may use them, but always same block types in L/R */
+ short_block_dispensed, /* LAME will not use short blocks, long blocks only */
+ short_block_forced /* LAME will not use long blocks, short blocks only */
+} short_block_t;
+
+/***********************************************************************
+*
+* Control Parameters set by User. These parameters are here for
+* backwards compatibility with the old, non-shared lib API.
+* Please use the lame_set_variablename() functions below
+*
+*
+***********************************************************************/
+struct lame_global_struct {
+ unsigned int class_id;
+
+ /* input description */
+ unsigned long num_samples; /* number of samples. default=2^32-1 */
+ int num_channels; /* input number of channels. default=2 */
+ int samplerate_in; /* input_samp_rate in Hz. default=44.1 kHz */
+ int samplerate_out; /* output_samp_rate.
+ default: LAME picks best value
+ at least not used for MP3 decoding:
+ Remember 44.1 kHz MP3s and AC97 */
+ float scale; /* scale input by this amount before encoding
+ at least not used for MP3 decoding */
+ float scale_left; /* scale input of channel 0 (left) by this
+ amount before encoding */
+ float scale_right; /* scale input of channel 1 (right) by this
+ amount before encoding */
+
+ /* general control params */
+ int analysis; /* collect data for a MP3 frame analyzer? */
+ int write_lame_tag; /* add Xing VBR tag? */
+ int decode_only; /* use lame/mpglib to convert mp3 to wav */
+ int quality; /* quality setting 0=best, 9=worst default=5 */
+ MPEG_mode mode; /* see enum in lame.h
+ default = LAME picks best value */
+ int force_ms; /* force M/S mode. requires mode=1 */
+ int free_format; /* use free format? default=0 */
+ int findReplayGain; /* find the RG value? default=0 */
+ int decode_on_the_fly; /* decode on the fly? default=0 */
+ int write_id3tag_automatic; /* 1 (default) writes ID3 tags, 0 not */
+
+ int nogap_total;
+ int nogap_current;
+
+ int substep_shaping;
+ int noise_shaping;
+ int subblock_gain; /* 0 = no, 1 = yes */
+ int use_best_huffman; /* 0 = no. 1=outside loop 2=inside loop(slow) */
+
+ /*
+ * set either brate>0 or compression_ratio>0, LAME will compute
+ * the value of the variable not set.
+ * Default is compression_ratio = 11.025
+ */
+ int brate; /* bitrate */
+ float compression_ratio; /* sizeof(wav file)/sizeof(mp3 file) */
+
+
+ /* frame params */
+ int copyright; /* mark as copyright. default=0 */
+ int original; /* mark as original. default=1 */
+ int extension; /* the MP3 'private extension' bit.
+ Meaningless */
+ int emphasis; /* Input PCM is emphased PCM (for
+ instance from one of the rarely
+ emphased CDs), it is STRONGLY not
+ recommended to use this, because
+ psycho does not take it into account,
+ and last but not least many decoders
+ don't care about these bits */
+ int error_protection; /* use 2 bytes per frame for a CRC
+ checksum. default=0 */
+ int strict_ISO; /* enforce ISO spec as much as possible */
+
+ int disable_reservoir; /* use bit reservoir? */
+
+ /* quantization/noise shaping */
+ int quant_comp;
+ int quant_comp_short;
+ int experimentalY;
+ int experimentalZ;
+ int exp_nspsytune;
+
+ int preset;
+
+ /* VBR control */
+ vbr_mode VBR;
+ float VBR_q_frac; /* Range [0,...,1[ */
+ int VBR_q; /* Range [0,...,9] */
+ int VBR_mean_bitrate_kbps;
+ int VBR_min_bitrate_kbps;
+ int VBR_max_bitrate_kbps;
+ int VBR_hard_min; /* strictly enforce VBR_min_bitrate
+ normaly, it will be violated for analog
+ silence */
+
+
+ /* resampling and filtering */
+ int lowpassfreq; /* freq in Hz. 0=lame choses.
+ -1=no filter */
+ int highpassfreq; /* freq in Hz. 0=lame choses.
+ -1=no filter */
+ int lowpasswidth; /* freq width of filter, in Hz
+ (default=15%) */
+ int highpasswidth; /* freq width of filter, in Hz
+ (default=15%) */
+
+
+
+ /*
+ * psycho acoustics and other arguments which you should not change
+ * unless you know what you are doing
+ */
+ float maskingadjust;
+ float maskingadjust_short;
+ int ATHonly; /* only use ATH */
+ int ATHshort; /* only use ATH for short blocks */
+ int noATH; /* disable ATH */
+ int ATHtype; /* select ATH formula */
+ float ATHcurve; /* change ATH formula 4 shape */
+ float ATH_lower_db; /* lower ATH by this many db */
+ int athaa_type; /* select ATH auto-adjust scheme */
+ float athaa_sensitivity; /* dB, tune active region of auto-level */
+ short_block_t short_blocks;
+ int useTemporal; /* use temporal masking effect */
+ float interChRatio;
+ float msfix; /* Naoki's adjustment of Mid/Side maskings */
+
+ int tune; /* 0 off, 1 on */
+ float tune_value_a; /* used to pass values for debugging and stuff */
+
+ float attackthre; /* attack threshold for L/R/M channel */
+ float attackthre_s; /* attack threshold for S channel */
+
+
+ struct {
+ void (*msgf) (const char *format, va_list ap);
+ void (*debugf) (const char *format, va_list ap);
+ void (*errorf) (const char *format, va_list ap);
+ } report;
+
+ /************************************************************************/
+ /* internal variables, do not set... */
+ /* provided because they may be of use to calling application */
+ /************************************************************************/
+
+ int lame_allocated_gfp; /* is this struct owned by calling
+ program or lame? */
+
+
+
+ /**************************************************************************/
+ /* more internal variables are stored in this structure: */
+ /**************************************************************************/
+ lame_internal_flags *internal_flags;
+
+
+ struct {
+ int mmx;
+ int amd3dnow;
+ int sse;
+
+ } asm_optimizations;
+};
+
+int is_lame_global_flags_valid(const lame_global_flags * gfp);
+
+#endif /* LAME_GLOBAL_FLAGS_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/lameerror.h b/libnative/src/main/cpp/module/mp3/lame/lameerror.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d9216bc45088afaae5de5da8228f83c719ce935
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/lameerror.h
@@ -0,0 +1,26 @@
+/*
+ * A collection of LAME Error Codes
+ *
+ * Please use the constants defined here instead of some arbitrary
+ * values. Currently the values starting at -10 to avoid intersection
+ * with the -1, -2, -3 and -4 used in the current code.
+ *
+ * May be this should be a part of the include/lame.h.
+ */
+
+typedef enum {
+ LAME_OKAY = 0,
+ LAME_NOERROR = 0,
+ LAME_GENERICERROR = -1,
+ LAME_NOMEM = -10,
+ LAME_BADBITRATE = -11,
+ LAME_BADSAMPFREQ = -12,
+ LAME_INTERNALERROR = -13,
+
+ FRONTEND_READERROR = -80,
+ FRONTEND_WRITEERROR = -81,
+ FRONTEND_FILETOOLARGE = -82,
+
+} lame_errorcodes_t;
+
+/* end of lameerror.h */
diff --git a/libnative/src/main/cpp/module/mp3/lame/machine.h b/libnative/src/main/cpp/module/mp3/lame/machine.h
new file mode 100644
index 0000000000000000000000000000000000000000..83a07a128b9b53a10c4255b37936a6423b94f564
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/machine.h
@@ -0,0 +1,188 @@
+/*
+ * Machine dependent defines/includes for LAME.
+ *
+ * Copyright (c) 1999 A.L. Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_MACHINE_H
+#define LAME_MACHINE_H
+
+#include "version.h"
+
+#if (LAME_RELEASE_VERSION == 0)
+#undef NDEBUG
+#endif
+
+#include
+#include
+
+#ifdef STDC_HEADERS
+# include
+# include
+#else
+//# ifndef HAVE_STRCHR
+//# define strchr index
+//# define strrchr rindex
+//# endif
+//char *strchr(), *strrchr();
+//# ifndef HAVE_MEMCPY
+//# define memcpy(d, s, n) bcopy ((s), (d), (n))
+//# define memmove(d, s, n) bcopy ((s), (d), (n))
+//# endif
+#endif
+
+#if defined(__riscos__) && defined(FPA10)
+# include "ymath.h"
+#else
+# include
+#endif
+#include
+
+#include
+
+#ifdef HAVE_ERRNO_H
+# include
+#endif
+#ifdef HAVE_FCNTL_H
+# include
+#endif
+
+#if defined(macintosh)
+# include
+# include
+#else
+# include
+# include
+#endif
+
+#ifdef HAVE_INTTYPES_H
+# include
+#else
+# ifdef HAVE_STDINT_H
+# include
+# endif
+#endif
+
+#ifdef WITH_DMALLOC
+#include
+#endif
+
+/*
+ * 3 different types of pow() functions:
+ * - table lookup
+ * - pow()
+ * - exp() on some machines this is claimed to be faster than pow()
+ */
+
+#define POW20(x) (assert(0 <= (x+Q_MAX2) && x < Q_MAX), pow20[x+Q_MAX2])
+/*#define POW20(x) pow(2.0,((double)(x)-210)*.25) */
+/*#define POW20(x) exp( ((double)(x)-210)*(.25*LOG2) ) */
+
+#define IPOW20(x) (assert(0 <= x && x < Q_MAX), ipow20[x])
+/*#define IPOW20(x) exp( -((double)(x)-210)*.1875*LOG2 ) */
+/*#define IPOW20(x) pow(2.0,-((double)(x)-210)*.1875) */
+
+/* in case this is used without configure */
+#ifndef inline
+# define inline
+#endif
+
+#if defined(_MSC_VER)
+# undef inline
+# define inline _inline
+#elif defined(__SASC) || defined(__GNUC__) || defined(__ICC) || defined(__ECC)
+/* if __GNUC__ we always want to inline, not only if the user requests it */
+# undef inline
+# define inline __inline
+#endif
+
+#if defined(_MSC_VER)
+# pragma warning( disable : 4244 )
+/*# pragma warning( disable : 4305 ) */
+#endif
+
+/*
+ * FLOAT for variables which require at least 32 bits
+ * FLOAT8 for variables which require at least 64 bits
+ *
+ * On some machines, 64 bit will be faster than 32 bit. Also, some math
+ * routines require 64 bit float, so setting FLOAT=float will result in a
+ * lot of conversions.
+ */
+
+#if ( defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) )
+# define WIN32_LEAN_AND_MEAN
+# include
+# include
+# define FLOAT_MAX FLT_MAX
+#else
+# ifndef FLOAT
+typedef float FLOAT;
+# ifdef FLT_MAX
+# define FLOAT_MAX FLT_MAX
+# else
+# define FLOAT_MAX 1e37 /* approx */
+# endif
+# endif
+#endif
+
+#ifndef FLOAT8
+typedef double FLOAT8;
+# ifdef DBL_MAX
+# define FLOAT8_MAX DBL_MAX
+# else
+# define FLOAT8_MAX 1e99 /* approx */
+# endif
+#else
+# ifdef FLT_MAX
+# define FLOAT8_MAX FLT_MAX
+# else
+# define FLOAT8_MAX 1e37 /* approx */
+# endif
+#endif
+
+/* sample_t must be floating point, at least 32 bits */
+typedef FLOAT sample_t;
+
+#define dimension_of(array) (sizeof(array)/sizeof(array[0]))
+#define beyond(array) (array+dimension_of(array))
+#define compiletime_assert(expression) extern char static_assert_##FILE##_##LINE[expression?1:0]
+
+#if 1
+#define EQ(a,b) (\
+(fabs(a) > fabs(b)) \
+ ? (fabs((a)-(b)) <= (fabs(a) * 1e-6f)) \
+ : (fabs((a)-(b)) <= (fabs(b) * 1e-6f)))
+#else
+#define EQ(a,b) (fabs((a)-(b))<1E-37)
+#endif
+
+#define NEQ(a,b) (!EQ(a,b))
+
+#endif
+
+#ifdef _MSC_VER
+# if _MSC_VER < 1400
+# define fabsf fabs
+# define powf pow
+# define log10f log10
+# endif
+#endif
+
+
+/* end of machine.h */
diff --git a/libnative/src/main/cpp/module/mp3/lame/mpglib_interface.c b/libnative/src/main/cpp/module/mp3/lame/mpglib_interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b90211b210d1f8e6632fc0996acfb4ea02aa855
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/mpglib_interface.c
@@ -0,0 +1,476 @@
+/* -*- mode: C; mode: fold -*- */
+/*
+ * LAME MP3 encoding engine
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 2003 Olcios
+ * Copyright (c) 2008 Robert Hegemann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: mpglib_interface.c,v 1.42 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#ifdef HAVE_MPGLIB
+#define hip_global_struct mpstr_tag
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "interface.h"
+
+#include "util.h"
+
+
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+/*
+ * OBSOLETE:
+ * - kept to let it link
+ * - forward declaration to silence compiler
+ */
+int CDECL lame_decode_init(void);
+int CDECL lame_decode(
+ unsigned char * mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[] );
+int CDECL lame_decode_headers(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data );
+int CDECL lame_decode1(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[] );
+int CDECL lame_decode1_headers(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data );
+int CDECL lame_decode1_headersB(
+ unsigned char* mp3buf,
+ int len,
+ short pcm_l[],
+ short pcm_r[],
+ mp3data_struct* mp3data,
+ int *enc_delay,
+ int *enc_padding );
+int CDECL lame_decode_exit(void);
+#endif
+
+
+static MPSTR mp;
+
+int
+lame_decode_exit(void)
+{
+ ExitMP3(&mp);
+ return 0;
+}
+
+
+int
+lame_decode_init(void)
+{
+ (void) InitMP3(&mp);
+ return 0;
+}
+
+
+
+
+/* copy mono samples */
+#define COPY_MONO(DST_TYPE, SRC_TYPE) \
+ DST_TYPE *pcm_l = (DST_TYPE *)pcm_l_raw; \
+ SRC_TYPE const *p_samples = (SRC_TYPE const *)p; \
+ for (i = 0; i < processed_samples; i++) \
+ *pcm_l++ = (DST_TYPE)(*p_samples++);
+
+/* copy stereo samples */
+#define COPY_STEREO(DST_TYPE, SRC_TYPE) \
+ DST_TYPE *pcm_l = (DST_TYPE *)pcm_l_raw, *pcm_r = (DST_TYPE *)pcm_r_raw; \
+ SRC_TYPE const *p_samples = (SRC_TYPE const *)p; \
+ for (i = 0; i < processed_samples; i++) { \
+ *pcm_l++ = (DST_TYPE)(*p_samples++); \
+ *pcm_r++ = (DST_TYPE)(*p_samples++); \
+ }
+
+
+
+/*
+ * For lame_decode: return code
+ * -1 error
+ * 0 ok, but need more data before outputing any samples
+ * n number of samples output. either 576 or 1152 depending on MP3 file.
+ */
+
+static int
+decode1_headersB_clipchoice(PMPSTR pmp, unsigned char *buffer, int len,
+ char pcm_l_raw[], char pcm_r_raw[], mp3data_struct * mp3data,
+ int *enc_delay, int *enc_padding,
+ char *p, size_t psize, int decoded_sample_size,
+ int (*decodeMP3_ptr) (PMPSTR, unsigned char *, int, char *, int,
+ int *))
+{
+ static const int smpls[2][4] = {
+ /* Layer I II III */
+ {0, 384, 1152, 1152}, /* MPEG-1 */
+ {0, 384, 1152, 576} /* MPEG-2(.5) */
+ };
+
+ int processed_bytes;
+ int processed_samples; /* processed samples per channel */
+ int ret;
+ int i;
+
+ mp3data->header_parsed = 0;
+
+ ret = (*decodeMP3_ptr) (pmp, buffer, len, p, (int) psize, &processed_bytes);
+ /* three cases:
+ * 1. headers parsed, but data not complete
+ * pmp->header_parsed==1
+ * pmp->framesize=0
+ * pmp->fsizeold=size of last frame, or 0 if this is first frame
+ *
+ * 2. headers, data parsed, but ancillary data not complete
+ * pmp->header_parsed==1
+ * pmp->framesize=size of frame
+ * pmp->fsizeold=size of last frame, or 0 if this is first frame
+ *
+ * 3. frame fully decoded:
+ * pmp->header_parsed==0
+ * pmp->framesize=0
+ * pmp->fsizeold=size of frame (which is now the last frame)
+ *
+ */
+ if (pmp->header_parsed || pmp->fsizeold > 0 || pmp->framesize > 0) {
+ mp3data->header_parsed = 1;
+ mp3data->stereo = pmp->fr.stereo;
+ mp3data->samplerate = freqs[pmp->fr.sampling_frequency];
+ mp3data->mode = pmp->fr.mode;
+ mp3data->mode_ext = pmp->fr.mode_ext;
+ mp3data->framesize = smpls[pmp->fr.lsf][pmp->fr.lay];
+
+ /* free format, we need the entire frame before we can determine
+ * the bitrate. If we haven't gotten the entire frame, bitrate=0 */
+ if (pmp->fsizeold > 0) /* works for free format and fixed, no overrun, temporal results are < 400.e6 */
+ mp3data->bitrate = 8 * (4 + pmp->fsizeold) * mp3data->samplerate /
+ (1.e3 * mp3data->framesize) + 0.5;
+ else if (pmp->framesize > 0)
+ mp3data->bitrate = 8 * (4 + pmp->framesize) * mp3data->samplerate /
+ (1.e3 * mp3data->framesize) + 0.5;
+ else
+ mp3data->bitrate = tabsel_123[pmp->fr.lsf][pmp->fr.lay - 1][pmp->fr.bitrate_index];
+
+
+
+ if (pmp->num_frames > 0) {
+ /* Xing VBR header found and num_frames was set */
+ mp3data->totalframes = pmp->num_frames;
+ mp3data->nsamp = mp3data->framesize * pmp->num_frames;
+ *enc_delay = pmp->enc_delay;
+ *enc_padding = pmp->enc_padding;
+ }
+ }
+
+ switch (ret) {
+ case MP3_OK:
+ switch (pmp->fr.stereo) {
+ case 1:
+ processed_samples = processed_bytes / decoded_sample_size;
+ if (decoded_sample_size == sizeof(short)) {
+ COPY_MONO(short, short)
+ }
+ else {
+ COPY_MONO(sample_t, FLOAT)
+ }
+ break;
+ case 2:
+ processed_samples = (processed_bytes / decoded_sample_size) >> 1;
+ if (decoded_sample_size == sizeof(short)) {
+ COPY_STEREO(short, short)
+ }
+ else {
+ COPY_STEREO(sample_t, FLOAT)
+ }
+ break;
+ default:
+ processed_samples = -1;
+ assert(0);
+ break;
+ }
+ break;
+
+ case MP3_NEED_MORE:
+ processed_samples = 0;
+ break;
+
+ case MP3_ERR:
+ processed_samples = -1;
+ break;
+
+ default:
+ processed_samples = -1;
+ assert(0);
+ break;
+ }
+
+ /*fprintf(stderr,"ok, more, err: %i %i %i\n", MP3_OK, MP3_NEED_MORE, MP3_ERR ); */
+ /*fprintf(stderr,"ret = %i out=%i\n", ret, processed_samples ); */
+ return processed_samples;
+}
+
+
+#define OUTSIZE_CLIPPED (4096*sizeof(short))
+
+int
+lame_decode1_headersB(unsigned char *buffer,
+ int len,
+ short pcm_l[], short pcm_r[], mp3data_struct * mp3data,
+ int *enc_delay, int *enc_padding)
+{
+ static char out[OUTSIZE_CLIPPED];
+
+ return decode1_headersB_clipchoice(&mp, buffer, len, (char *) pcm_l, (char *) pcm_r, mp3data,
+ enc_delay, enc_padding, out, OUTSIZE_CLIPPED,
+ sizeof(short), decodeMP3);
+}
+
+
+
+
+
+/*
+ * For lame_decode: return code
+ * -1 error
+ * 0 ok, but need more data before outputing any samples
+ * n number of samples output. Will be at most one frame of
+ * MPEG data.
+ */
+
+int
+lame_decode1_headers(unsigned char *buffer,
+ int len, short pcm_l[], short pcm_r[], mp3data_struct * mp3data)
+{
+ int enc_delay, enc_padding;
+ return lame_decode1_headersB(buffer, len, pcm_l, pcm_r, mp3data, &enc_delay, &enc_padding);
+}
+
+
+int
+lame_decode1(unsigned char *buffer, int len, short pcm_l[], short pcm_r[])
+{
+ mp3data_struct mp3data;
+
+ return lame_decode1_headers(buffer, len, pcm_l, pcm_r, &mp3data);
+}
+
+
+/*
+ * For lame_decode: return code
+ * -1 error
+ * 0 ok, but need more data before outputing any samples
+ * n number of samples output. a multiple of 576 or 1152 depending on MP3 file.
+ */
+
+int
+lame_decode_headers(unsigned char *buffer,
+ int len, short pcm_l[], short pcm_r[], mp3data_struct * mp3data)
+{
+ int ret;
+ int totsize = 0; /* number of decoded samples per channel */
+
+ for (;;) {
+ switch (ret = lame_decode1_headers(buffer, len, pcm_l + totsize, pcm_r + totsize, mp3data)) {
+ case -1:
+ return ret;
+ case 0:
+ return totsize;
+ default:
+ totsize += ret;
+ len = 0; /* future calls to decodeMP3 are just to flush buffers */
+ break;
+ }
+ }
+}
+
+
+int
+lame_decode(unsigned char *buffer, int len, short pcm_l[], short pcm_r[])
+{
+ mp3data_struct mp3data;
+
+ return lame_decode_headers(buffer, len, pcm_l, pcm_r, &mp3data);
+}
+
+
+
+
+hip_t hip_decode_init(void)
+{
+ hip_t hip = calloc(1, sizeof(hip_global_flags));
+ InitMP3(hip);
+ return hip;
+}
+
+
+int hip_decode_exit(hip_t hip)
+{
+ if (hip) {
+ ExitMP3(hip);
+ free(hip);
+ }
+ return 0;
+}
+
+
+/* we forbid input with more than 1152 samples per channel for output in the unclipped mode */
+#define OUTSIZE_UNCLIPPED (1152*2*sizeof(FLOAT))
+
+int
+hip_decode1_unclipped(hip_t hip, unsigned char *buffer, size_t len, sample_t pcm_l[], sample_t pcm_r[])
+{
+ static char out[OUTSIZE_UNCLIPPED];
+ mp3data_struct mp3data;
+ int enc_delay, enc_padding;
+
+ if (hip) {
+ return decode1_headersB_clipchoice(hip, buffer, len, (char *) pcm_l, (char *) pcm_r, &mp3data,
+ &enc_delay, &enc_padding, out, OUTSIZE_UNCLIPPED,
+ sizeof(FLOAT), decodeMP3_unclipped);
+ }
+ return 0;
+}
+
+/*
+ * For hip_decode: return code
+ * -1 error
+ * 0 ok, but need more data before outputing any samples
+ * n number of samples output. Will be at most one frame of
+ * MPEG data.
+ */
+
+int
+hip_decode1_headers(hip_t hip, unsigned char *buffer,
+ size_t len, short pcm_l[], short pcm_r[], mp3data_struct * mp3data)
+{
+ int enc_delay, enc_padding;
+ return hip_decode1_headersB(hip, buffer, len, pcm_l, pcm_r, mp3data, &enc_delay, &enc_padding);
+}
+
+
+int
+hip_decode1(hip_t hip, unsigned char *buffer, size_t len, short pcm_l[], short pcm_r[])
+{
+ mp3data_struct mp3data;
+ return hip_decode1_headers(hip, buffer, len, pcm_l, pcm_r, &mp3data);
+}
+
+
+/*
+ * For hip_decode: return code
+ * -1 error
+ * 0 ok, but need more data before outputing any samples
+ * n number of samples output. a multiple of 576 or 1152 depending on MP3 file.
+ */
+
+int
+hip_decode_headers(hip_t hip, unsigned char *buffer,
+ size_t len, short pcm_l[], short pcm_r[], mp3data_struct * mp3data)
+{
+ int ret;
+ int totsize = 0; /* number of decoded samples per channel */
+
+ for (;;) {
+ switch (ret = hip_decode1_headers(hip, buffer, len, pcm_l + totsize, pcm_r + totsize, mp3data)) {
+ case -1:
+ return ret;
+ case 0:
+ return totsize;
+ default:
+ totsize += ret;
+ len = 0; /* future calls to decodeMP3 are just to flush buffers */
+ break;
+ }
+ }
+}
+
+
+int
+hip_decode(hip_t hip, unsigned char *buffer, size_t len, short pcm_l[], short pcm_r[])
+{
+ mp3data_struct mp3data;
+ return hip_decode_headers(hip, buffer, len, pcm_l, pcm_r, &mp3data);
+}
+
+
+int
+hip_decode1_headersB(hip_t hip, unsigned char *buffer,
+ size_t len,
+ short pcm_l[], short pcm_r[], mp3data_struct * mp3data,
+ int *enc_delay, int *enc_padding)
+{
+ static char out[OUTSIZE_CLIPPED];
+ if (hip) {
+ return decode1_headersB_clipchoice(hip, buffer, len, (char *) pcm_l, (char *) pcm_r, mp3data,
+ enc_delay, enc_padding, out, OUTSIZE_CLIPPED,
+ sizeof(short), decodeMP3);
+ }
+ return -1;
+}
+
+
+void hip_set_pinfo(hip_t hip, plotting_data* pinfo)
+{
+ if (hip) {
+ hip->pinfo = pinfo;
+ }
+}
+
+
+
+void hip_set_errorf(hip_t hip, lame_report_function func)
+{
+ if (hip) {
+ hip->report_err = func;
+ }
+}
+
+void hip_set_debugf(hip_t hip, lame_report_function func)
+{
+ if (hip) {
+ hip->report_dbg = func;
+ }
+}
+
+void hip_set_msgf (hip_t hip, lame_report_function func)
+{
+ if (hip) {
+ hip->report_msg = func;
+ }
+}
+
+#endif
+
+/* end of mpglib_interface.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/newmdct.c b/libnative/src/main/cpp/module/mp3/lame/newmdct.c
new file mode 100644
index 0000000000000000000000000000000000000000..596cac919da22889cad11076ca15b5b667d073d3
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/newmdct.c
@@ -0,0 +1,1039 @@
+/*
+ * MP3 window subband -> subband filtering -> mdct routine
+ *
+ * Copyright (c) 1999-2000 Takehiro Tominaga
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Special Thanks to Patrick De Smet for your advices.
+ */
+
+/* $Id: newmdct.c,v 1.39 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "newmdct.h"
+
+
+
+#ifndef USE_GOGO_SUBBAND
+static const FLOAT enwindow[] = {
+ -4.77e-07 * 0.740951125354959 / 2.384e-06, 1.03951e-04 * 0.740951125354959 / 2.384e-06,
+ 9.53674e-04 * 0.740951125354959 / 2.384e-06, 2.841473e-03 * 0.740951125354959 / 2.384e-06,
+ 3.5758972e-02 * 0.740951125354959 / 2.384e-06, 3.401756e-03 * 0.740951125354959 / 2.384e-06, 9.83715e-04 * 0.740951125354959 / 2.384e-06, 9.9182e-05 * 0.740951125354959 / 2.384e-06, /* 15 */
+ 1.2398e-05 * 0.740951125354959 / 2.384e-06, 1.91212e-04 * 0.740951125354959 / 2.384e-06,
+ 2.283096e-03 * 0.740951125354959 / 2.384e-06, 1.6994476e-02 * 0.740951125354959 / 2.384e-06,
+ -1.8756866e-02 * 0.740951125354959 / 2.384e-06, -2.630711e-03 * 0.740951125354959 / 2.384e-06,
+ -2.47478e-04 * 0.740951125354959 / 2.384e-06, -1.4782e-05 * 0.740951125354959 / 2.384e-06,
+ 9.063471690191471e-01,
+ 1.960342806591213e-01,
+
+
+ -4.77e-07 * 0.773010453362737 / 2.384e-06, 1.05858e-04 * 0.773010453362737 / 2.384e-06,
+ 9.30786e-04 * 0.773010453362737 / 2.384e-06, 2.521515e-03 * 0.773010453362737 / 2.384e-06,
+ 3.5694122e-02 * 0.773010453362737 / 2.384e-06, 3.643036e-03 * 0.773010453362737 / 2.384e-06, 9.91821e-04 * 0.773010453362737 / 2.384e-06, 9.6321e-05 * 0.773010453362737 / 2.384e-06, /* 14 */
+ 1.1444e-05 * 0.773010453362737 / 2.384e-06, 1.65462e-04 * 0.773010453362737 / 2.384e-06,
+ 2.110004e-03 * 0.773010453362737 / 2.384e-06, 1.6112804e-02 * 0.773010453362737 / 2.384e-06,
+ -1.9634247e-02 * 0.773010453362737 / 2.384e-06, -2.803326e-03 * 0.773010453362737 / 2.384e-06,
+ -2.77042e-04 * 0.773010453362737 / 2.384e-06, -1.6689e-05 * 0.773010453362737 / 2.384e-06,
+ 8.206787908286602e-01,
+ 3.901806440322567e-01,
+
+
+ -4.77e-07 * 0.803207531480645 / 2.384e-06, 1.07288e-04 * 0.803207531480645 / 2.384e-06,
+ 9.02653e-04 * 0.803207531480645 / 2.384e-06, 2.174854e-03 * 0.803207531480645 / 2.384e-06,
+ 3.5586357e-02 * 0.803207531480645 / 2.384e-06, 3.858566e-03 * 0.803207531480645 / 2.384e-06, 9.95159e-04 * 0.803207531480645 / 2.384e-06, 9.3460e-05 * 0.803207531480645 / 2.384e-06, /* 13 */
+ 1.0014e-05 * 0.803207531480645 / 2.384e-06, 1.40190e-04 * 0.803207531480645 / 2.384e-06,
+ 1.937389e-03 * 0.803207531480645 / 2.384e-06, 1.5233517e-02 * 0.803207531480645 / 2.384e-06,
+ -2.0506859e-02 * 0.803207531480645 / 2.384e-06, -2.974033e-03 * 0.803207531480645 / 2.384e-06,
+ -3.07560e-04 * 0.803207531480645 / 2.384e-06, -1.8120e-05 * 0.803207531480645 / 2.384e-06,
+ 7.416505462720353e-01,
+ 5.805693545089249e-01,
+
+
+ -4.77e-07 * 0.831469612302545 / 2.384e-06, 1.08242e-04 * 0.831469612302545 / 2.384e-06,
+ 8.68797e-04 * 0.831469612302545 / 2.384e-06, 1.800537e-03 * 0.831469612302545 / 2.384e-06,
+ 3.5435200e-02 * 0.831469612302545 / 2.384e-06, 4.049301e-03 * 0.831469612302545 / 2.384e-06, 9.94205e-04 * 0.831469612302545 / 2.384e-06, 9.0599e-05 * 0.831469612302545 / 2.384e-06, /* 12 */
+ 9.060e-06 * 0.831469612302545 / 2.384e-06, 1.16348e-04 * 0.831469612302545 / 2.384e-06,
+ 1.766682e-03 * 0.831469612302545 / 2.384e-06, 1.4358521e-02 * 0.831469612302545 / 2.384e-06,
+ -2.1372318e-02 * 0.831469612302545 / 2.384e-06, -3.14188e-03 * 0.831469612302545 / 2.384e-06,
+ -3.39031e-04 * 0.831469612302545 / 2.384e-06, -1.9550e-05 * 0.831469612302545 / 2.384e-06,
+ 6.681786379192989e-01,
+ 7.653668647301797e-01,
+
+
+ -4.77e-07 * 0.857728610000272 / 2.384e-06, 1.08719e-04 * 0.857728610000272 / 2.384e-06,
+ 8.29220e-04 * 0.857728610000272 / 2.384e-06, 1.399517e-03 * 0.857728610000272 / 2.384e-06,
+ 3.5242081e-02 * 0.857728610000272 / 2.384e-06, 4.215240e-03 * 0.857728610000272 / 2.384e-06, 9.89437e-04 * 0.857728610000272 / 2.384e-06, 8.7261e-05 * 0.857728610000272 / 2.384e-06, /* 11 */
+ 8.106e-06 * 0.857728610000272 / 2.384e-06, 9.3937e-05 * 0.857728610000272 / 2.384e-06,
+ 1.597881e-03 * 0.857728610000272 / 2.384e-06, 1.3489246e-02 * 0.857728610000272 / 2.384e-06,
+ -2.2228718e-02 * 0.857728610000272 / 2.384e-06, -3.306866e-03 * 0.857728610000272 / 2.384e-06,
+ -3.71456e-04 * 0.857728610000272 / 2.384e-06, -2.1458e-05 * 0.857728610000272 / 2.384e-06,
+ 5.993769336819237e-01,
+ 9.427934736519954e-01,
+
+
+ -4.77e-07 * 0.881921264348355 / 2.384e-06, 1.08719e-04 * 0.881921264348355 / 2.384e-06,
+ 7.8392e-04 * 0.881921264348355 / 2.384e-06, 9.71317e-04 * 0.881921264348355 / 2.384e-06,
+ 3.5007000e-02 * 0.881921264348355 / 2.384e-06, 4.357815e-03 * 0.881921264348355 / 2.384e-06, 9.80854e-04 * 0.881921264348355 / 2.384e-06, 8.3923e-05 * 0.881921264348355 / 2.384e-06, /* 10 */
+ 7.629e-06 * 0.881921264348355 / 2.384e-06, 7.2956e-05 * 0.881921264348355 / 2.384e-06,
+ 1.432419e-03 * 0.881921264348355 / 2.384e-06, 1.2627602e-02 * 0.881921264348355 / 2.384e-06,
+ -2.3074150e-02 * 0.881921264348355 / 2.384e-06, -3.467083e-03 * 0.881921264348355 / 2.384e-06,
+ -4.04358e-04 * 0.881921264348355 / 2.384e-06, -2.3365e-05 * 0.881921264348355 / 2.384e-06,
+ 5.345111359507916e-01,
+ 1.111140466039205e+00,
+
+
+ -9.54e-07 * 0.903989293123443 / 2.384e-06, 1.08242e-04 * 0.903989293123443 / 2.384e-06,
+ 7.31945e-04 * 0.903989293123443 / 2.384e-06, 5.15938e-04 * 0.903989293123443 / 2.384e-06,
+ 3.4730434e-02 * 0.903989293123443 / 2.384e-06, 4.477024e-03 * 0.903989293123443 / 2.384e-06, 9.68933e-04 * 0.903989293123443 / 2.384e-06, 8.0585e-05 * 0.903989293123443 / 2.384e-06, /* 9 */
+ 6.676e-06 * 0.903989293123443 / 2.384e-06, 5.2929e-05 * 0.903989293123443 / 2.384e-06,
+ 1.269817e-03 * 0.903989293123443 / 2.384e-06, 1.1775017e-02 * 0.903989293123443 / 2.384e-06,
+ -2.3907185e-02 * 0.903989293123443 / 2.384e-06, -3.622532e-03 * 0.903989293123443 / 2.384e-06,
+ -4.38213e-04 * 0.903989293123443 / 2.384e-06, -2.5272e-05 * 0.903989293123443 / 2.384e-06,
+ 4.729647758913199e-01,
+ 1.268786568327291e+00,
+
+
+ -9.54e-07 * 0.92387953251128675613 / 2.384e-06,
+ 1.06812e-04 * 0.92387953251128675613 / 2.384e-06,
+ 6.74248e-04 * 0.92387953251128675613 / 2.384e-06,
+ 3.3379e-05 * 0.92387953251128675613 / 2.384e-06,
+ 3.4412861e-02 * 0.92387953251128675613 / 2.384e-06,
+ 4.573822e-03 * 0.92387953251128675613 / 2.384e-06,
+ 9.54151e-04 * 0.92387953251128675613 / 2.384e-06,
+ 7.6771e-05 * 0.92387953251128675613 / 2.384e-06,
+ 6.199e-06 * 0.92387953251128675613 / 2.384e-06, 3.4332e-05 * 0.92387953251128675613 / 2.384e-06,
+ 1.111031e-03 * 0.92387953251128675613 / 2.384e-06,
+ 1.0933399e-02 * 0.92387953251128675613 / 2.384e-06,
+ -2.4725437e-02 * 0.92387953251128675613 / 2.384e-06,
+ -3.771782e-03 * 0.92387953251128675613 / 2.384e-06,
+ -4.72546e-04 * 0.92387953251128675613 / 2.384e-06,
+ -2.7657e-05 * 0.92387953251128675613 / 2.384e-06,
+ 4.1421356237309504879e-01, /* tan(PI/8) */
+ 1.414213562373095e+00,
+
+
+ -9.54e-07 * 0.941544065183021 / 2.384e-06, 1.05381e-04 * 0.941544065183021 / 2.384e-06,
+ 6.10352e-04 * 0.941544065183021 / 2.384e-06, -4.75883e-04 * 0.941544065183021 / 2.384e-06,
+ 3.4055710e-02 * 0.941544065183021 / 2.384e-06, 4.649162e-03 * 0.941544065183021 / 2.384e-06, 9.35555e-04 * 0.941544065183021 / 2.384e-06, 7.3433e-05 * 0.941544065183021 / 2.384e-06, /* 7 */
+ 5.245e-06 * 0.941544065183021 / 2.384e-06, 1.7166e-05 * 0.941544065183021 / 2.384e-06,
+ 9.56535e-04 * 0.941544065183021 / 2.384e-06, 1.0103703e-02 * 0.941544065183021 / 2.384e-06,
+ -2.5527000e-02 * 0.941544065183021 / 2.384e-06, -3.914356e-03 * 0.941544065183021 / 2.384e-06,
+ -5.07355e-04 * 0.941544065183021 / 2.384e-06, -3.0041e-05 * 0.941544065183021 / 2.384e-06,
+ 3.578057213145241e-01,
+ 1.546020906725474e+00,
+
+
+ -9.54e-07 * 0.956940335732209 / 2.384e-06, 1.02520e-04 * 0.956940335732209 / 2.384e-06,
+ 5.39303e-04 * 0.956940335732209 / 2.384e-06, -1.011848e-03 * 0.956940335732209 / 2.384e-06,
+ 3.3659935e-02 * 0.956940335732209 / 2.384e-06, 4.703045e-03 * 0.956940335732209 / 2.384e-06, 9.15051e-04 * 0.956940335732209 / 2.384e-06, 7.0095e-05 * 0.956940335732209 / 2.384e-06, /* 6 */
+ 4.768e-06 * 0.956940335732209 / 2.384e-06, 9.54e-07 * 0.956940335732209 / 2.384e-06,
+ 8.06808e-04 * 0.956940335732209 / 2.384e-06, 9.287834e-03 * 0.956940335732209 / 2.384e-06,
+ -2.6310921e-02 * 0.956940335732209 / 2.384e-06, -4.048824e-03 * 0.956940335732209 / 2.384e-06,
+ -5.42164e-04 * 0.956940335732209 / 2.384e-06, -3.2425e-05 * 0.956940335732209 / 2.384e-06,
+ 3.033466836073424e-01,
+ 1.662939224605090e+00,
+
+
+ -1.431e-06 * 0.970031253194544 / 2.384e-06, 9.9182e-05 * 0.970031253194544 / 2.384e-06,
+ 4.62532e-04 * 0.970031253194544 / 2.384e-06, -1.573563e-03 * 0.970031253194544 / 2.384e-06,
+ 3.3225536e-02 * 0.970031253194544 / 2.384e-06, 4.737377e-03 * 0.970031253194544 / 2.384e-06, 8.91685e-04 * 0.970031253194544 / 2.384e-06, 6.6280e-05 * 0.970031253194544 / 2.384e-06, /* 5 */
+ 4.292e-06 * 0.970031253194544 / 2.384e-06, -1.3828e-05 * 0.970031253194544 / 2.384e-06,
+ 6.61850e-04 * 0.970031253194544 / 2.384e-06, 8.487225e-03 * 0.970031253194544 / 2.384e-06,
+ -2.7073860e-02 * 0.970031253194544 / 2.384e-06, -4.174709e-03 * 0.970031253194544 / 2.384e-06,
+ -5.76973e-04 * 0.970031253194544 / 2.384e-06, -3.4809e-05 * 0.970031253194544 / 2.384e-06,
+ 2.504869601913055e-01,
+ 1.763842528696710e+00,
+
+
+ -1.431e-06 * 0.98078528040323 / 2.384e-06, 9.5367e-05 * 0.98078528040323 / 2.384e-06,
+ 3.78609e-04 * 0.98078528040323 / 2.384e-06, -2.161503e-03 * 0.98078528040323 / 2.384e-06,
+ 3.2754898e-02 * 0.98078528040323 / 2.384e-06, 4.752159e-03 * 0.98078528040323 / 2.384e-06, 8.66413e-04 * 0.98078528040323 / 2.384e-06, 6.2943e-05 * 0.98078528040323 / 2.384e-06, /* 4 */
+ 3.815e-06 * 0.98078528040323 / 2.384e-06, -2.718e-05 * 0.98078528040323 / 2.384e-06,
+ 5.22137e-04 * 0.98078528040323 / 2.384e-06, 7.703304e-03 * 0.98078528040323 / 2.384e-06,
+ -2.7815342e-02 * 0.98078528040323 / 2.384e-06, -4.290581e-03 * 0.98078528040323 / 2.384e-06,
+ -6.11782e-04 * 0.98078528040323 / 2.384e-06, -3.7670e-05 * 0.98078528040323 / 2.384e-06,
+ 1.989123673796580e-01,
+ 1.847759065022573e+00,
+
+
+ -1.907e-06 * 0.989176509964781 / 2.384e-06, 9.0122e-05 * 0.989176509964781 / 2.384e-06,
+ 2.88486e-04 * 0.989176509964781 / 2.384e-06, -2.774239e-03 * 0.989176509964781 / 2.384e-06,
+ 3.2248020e-02 * 0.989176509964781 / 2.384e-06, 4.748821e-03 * 0.989176509964781 / 2.384e-06, 8.38757e-04 * 0.989176509964781 / 2.384e-06, 5.9605e-05 * 0.989176509964781 / 2.384e-06, /* 3 */
+ 3.338e-06 * 0.989176509964781 / 2.384e-06, -3.9577e-05 * 0.989176509964781 / 2.384e-06,
+ 3.88145e-04 * 0.989176509964781 / 2.384e-06, 6.937027e-03 * 0.989176509964781 / 2.384e-06,
+ -2.8532982e-02 * 0.989176509964781 / 2.384e-06, -4.395962e-03 * 0.989176509964781 / 2.384e-06,
+ -6.46591e-04 * 0.989176509964781 / 2.384e-06, -4.0531e-05 * 0.989176509964781 / 2.384e-06,
+ 1.483359875383474e-01,
+ 1.913880671464418e+00,
+
+
+ -1.907e-06 * 0.995184726672197 / 2.384e-06, 8.4400e-05 * 0.995184726672197 / 2.384e-06,
+ 1.91689e-04 * 0.995184726672197 / 2.384e-06, -3.411293e-03 * 0.995184726672197 / 2.384e-06,
+ 3.1706810e-02 * 0.995184726672197 / 2.384e-06, 4.728317e-03 * 0.995184726672197 / 2.384e-06,
+ 8.09669e-04 * 0.995184726672197 / 2.384e-06, 5.579e-05 * 0.995184726672197 / 2.384e-06,
+ 3.338e-06 * 0.995184726672197 / 2.384e-06, -5.0545e-05 * 0.995184726672197 / 2.384e-06,
+ 2.59876e-04 * 0.995184726672197 / 2.384e-06, 6.189346e-03 * 0.995184726672197 / 2.384e-06,
+ -2.9224873e-02 * 0.995184726672197 / 2.384e-06, -4.489899e-03 * 0.995184726672197 / 2.384e-06,
+ -6.80923e-04 * 0.995184726672197 / 2.384e-06, -4.3392e-05 * 0.995184726672197 / 2.384e-06,
+ 9.849140335716425e-02,
+ 1.961570560806461e+00,
+
+
+ -2.384e-06 * 0.998795456205172 / 2.384e-06, 7.7724e-05 * 0.998795456205172 / 2.384e-06,
+ 8.8215e-05 * 0.998795456205172 / 2.384e-06, -4.072189e-03 * 0.998795456205172 / 2.384e-06,
+ 3.1132698e-02 * 0.998795456205172 / 2.384e-06, 4.691124e-03 * 0.998795456205172 / 2.384e-06,
+ 7.79152e-04 * 0.998795456205172 / 2.384e-06, 5.2929e-05 * 0.998795456205172 / 2.384e-06,
+ 2.861e-06 * 0.998795456205172 / 2.384e-06, -6.0558e-05 * 0.998795456205172 / 2.384e-06,
+ 1.37329e-04 * 0.998795456205172 / 2.384e-06, 5.462170e-03 * 0.998795456205172 / 2.384e-06,
+ -2.9890060e-02 * 0.998795456205172 / 2.384e-06, -4.570484e-03 * 0.998795456205172 / 2.384e-06,
+ -7.14302e-04 * 0.998795456205172 / 2.384e-06, -4.6253e-05 * 0.998795456205172 / 2.384e-06,
+ 4.912684976946725e-02,
+ 1.990369453344394e+00,
+
+
+ 3.5780907e-02 * SQRT2 * 0.5 / 2.384e-06, 1.7876148e-02 * SQRT2 * 0.5 / 2.384e-06,
+ 3.134727e-03 * SQRT2 * 0.5 / 2.384e-06, 2.457142e-03 * SQRT2 * 0.5 / 2.384e-06,
+ 9.71317e-04 * SQRT2 * 0.5 / 2.384e-06, 2.18868e-04 * SQRT2 * 0.5 / 2.384e-06,
+ 1.01566e-04 * SQRT2 * 0.5 / 2.384e-06, 1.3828e-05 * SQRT2 * 0.5 / 2.384e-06,
+
+ 3.0526638e-02 / 2.384e-06, 4.638195e-03 / 2.384e-06, 7.47204e-04 / 2.384e-06,
+ 4.9591e-05 / 2.384e-06,
+ 4.756451e-03 / 2.384e-06, 2.1458e-05 / 2.384e-06, -6.9618e-05 / 2.384e-06, /* 2.384e-06/2.384e-06 */
+};
+#endif
+
+
+#define NS 12
+#define NL 36
+
+static const FLOAT win[4][NL] = {
+ {
+ 2.382191739347913e-13,
+ 6.423305872147834e-13,
+ 9.400849094049688e-13,
+ 1.122435026096556e-12,
+ 1.183840321267481e-12,
+ 1.122435026096556e-12,
+ 9.400849094049690e-13,
+ 6.423305872147839e-13,
+ 2.382191739347918e-13,
+
+ 5.456116108943412e-12,
+ 4.878985199565852e-12,
+ 4.240448995017367e-12,
+ 3.559909094758252e-12,
+ 2.858043359288075e-12,
+ 2.156177623817898e-12,
+ 1.475637723558783e-12,
+ 8.371015190102974e-13,
+ 2.599706096327376e-13,
+
+ -5.456116108943412e-12,
+ -4.878985199565852e-12,
+ -4.240448995017367e-12,
+ -3.559909094758252e-12,
+ -2.858043359288076e-12,
+ -2.156177623817898e-12,
+ -1.475637723558783e-12,
+ -8.371015190102975e-13,
+ -2.599706096327376e-13,
+
+ -2.382191739347923e-13,
+ -6.423305872147843e-13,
+ -9.400849094049696e-13,
+ -1.122435026096556e-12,
+ -1.183840321267481e-12,
+ -1.122435026096556e-12,
+ -9.400849094049694e-13,
+ -6.423305872147840e-13,
+ -2.382191739347918e-13,
+ },
+ {
+ 2.382191739347913e-13,
+ 6.423305872147834e-13,
+ 9.400849094049688e-13,
+ 1.122435026096556e-12,
+ 1.183840321267481e-12,
+ 1.122435026096556e-12,
+ 9.400849094049688e-13,
+ 6.423305872147841e-13,
+ 2.382191739347918e-13,
+
+ 5.456116108943413e-12,
+ 4.878985199565852e-12,
+ 4.240448995017367e-12,
+ 3.559909094758253e-12,
+ 2.858043359288075e-12,
+ 2.156177623817898e-12,
+ 1.475637723558782e-12,
+ 8.371015190102975e-13,
+ 2.599706096327376e-13,
+
+ -5.461314069809755e-12,
+ -4.921085770524055e-12,
+ -4.343405037091838e-12,
+ -3.732668368707687e-12,
+ -3.093523840190885e-12,
+ -2.430835727329465e-12,
+ -1.734679010007751e-12,
+ -9.748253656609281e-13,
+ -2.797435120168326e-13,
+
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ -2.283748241799531e-13,
+ -4.037858874020686e-13,
+ -2.146547464825323e-13,
+ },
+ {
+ 1.316524975873958e-01, /* win[SHORT_TYPE] */
+ 4.142135623730950e-01,
+ 7.673269879789602e-01,
+
+ 1.091308501069271e+00, /* tantab_l */
+ 1.303225372841206e+00,
+ 1.569685577117490e+00,
+ 1.920982126971166e+00,
+ 2.414213562373094e+00,
+ 3.171594802363212e+00,
+ 4.510708503662055e+00,
+ 7.595754112725146e+00,
+ 2.290376554843115e+01,
+
+ 0.98480775301220802032, /* cx */
+ 0.64278760968653936292,
+ 0.34202014332566882393,
+ 0.93969262078590842791,
+ -0.17364817766693030343,
+ -0.76604444311897790243,
+ 0.86602540378443870761,
+ 0.500000000000000e+00,
+
+ -5.144957554275265e-01, /* ca */
+ -4.717319685649723e-01,
+ -3.133774542039019e-01,
+ -1.819131996109812e-01,
+ -9.457419252642064e-02,
+ -4.096558288530405e-02,
+ -1.419856857247115e-02,
+ -3.699974673760037e-03,
+
+ 8.574929257125442e-01, /* cs */
+ 8.817419973177052e-01,
+ 9.496286491027329e-01,
+ 9.833145924917901e-01,
+ 9.955178160675857e-01,
+ 9.991605581781475e-01,
+ 9.998991952444470e-01,
+ 9.999931550702802e-01,
+ },
+ {
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 0.000000000000000e+00,
+ 2.283748241799531e-13,
+ 4.037858874020686e-13,
+ 2.146547464825323e-13,
+
+ 5.461314069809755e-12,
+ 4.921085770524055e-12,
+ 4.343405037091838e-12,
+ 3.732668368707687e-12,
+ 3.093523840190885e-12,
+ 2.430835727329466e-12,
+ 1.734679010007751e-12,
+ 9.748253656609281e-13,
+ 2.797435120168326e-13,
+
+ -5.456116108943413e-12,
+ -4.878985199565852e-12,
+ -4.240448995017367e-12,
+ -3.559909094758253e-12,
+ -2.858043359288075e-12,
+ -2.156177623817898e-12,
+ -1.475637723558782e-12,
+ -8.371015190102975e-13,
+ -2.599706096327376e-13,
+
+ -2.382191739347913e-13,
+ -6.423305872147834e-13,
+ -9.400849094049688e-13,
+ -1.122435026096556e-12,
+ -1.183840321267481e-12,
+ -1.122435026096556e-12,
+ -9.400849094049688e-13,
+ -6.423305872147841e-13,
+ -2.382191739347918e-13,
+ }
+};
+
+#define tantab_l (win[SHORT_TYPE]+3)
+#define cx (win[SHORT_TYPE]+12)
+#define ca (win[SHORT_TYPE]+20)
+#define cs (win[SHORT_TYPE]+28)
+
+/************************************************************************
+*
+* window_subband()
+*
+* PURPOSE: Overlapping window on PCM samples
+*
+* SEMANTICS:
+* 32 16-bit pcm samples are scaled to fractional 2's complement and
+* concatenated to the end of the window buffer #x#. The updated window
+* buffer #x# is then windowed by the analysis window #c# to produce the
+* windowed sample #z#
+*
+************************************************************************/
+
+/*
+ * new IDCT routine written by Takehiro TOMINAGA
+ */
+static const int order[] = {
+ 0, 1, 16, 17, 8, 9, 24, 25, 4, 5, 20, 21, 12, 13, 28, 29,
+ 2, 3, 18, 19, 10, 11, 26, 27, 6, 7, 22, 23, 14, 15, 30, 31
+};
+
+
+/* returns sum_j=0^31 a[j]*cos(PI*j*(k+1/2)/32), 0<=k<32 */
+inline static void
+window_subband(const sample_t * x1, FLOAT a[SBLIMIT])
+{
+ int i;
+ FLOAT const *wp = enwindow + 10;
+
+ const sample_t *x2 = &x1[238 - 14 - 286];
+
+ for (i = -15; i < 0; i++) {
+ FLOAT w, s, t;
+
+ w = wp[-10];
+ s = x2[-224] * w;
+ t = x1[224] * w;
+ w = wp[-9];
+ s += x2[-160] * w;
+ t += x1[160] * w;
+ w = wp[-8];
+ s += x2[-96] * w;
+ t += x1[96] * w;
+ w = wp[-7];
+ s += x2[-32] * w;
+ t += x1[32] * w;
+ w = wp[-6];
+ s += x2[32] * w;
+ t += x1[-32] * w;
+ w = wp[-5];
+ s += x2[96] * w;
+ t += x1[-96] * w;
+ w = wp[-4];
+ s += x2[160] * w;
+ t += x1[-160] * w;
+ w = wp[-3];
+ s += x2[224] * w;
+ t += x1[-224] * w;
+
+ w = wp[-2];
+ s += x1[-256] * w;
+ t -= x2[256] * w;
+ w = wp[-1];
+ s += x1[-192] * w;
+ t -= x2[192] * w;
+ w = wp[0];
+ s += x1[-128] * w;
+ t -= x2[128] * w;
+ w = wp[1];
+ s += x1[-64] * w;
+ t -= x2[64] * w;
+ w = wp[2];
+ s += x1[0] * w;
+ t -= x2[0] * w;
+ w = wp[3];
+ s += x1[64] * w;
+ t -= x2[-64] * w;
+ w = wp[4];
+ s += x1[128] * w;
+ t -= x2[-128] * w;
+ w = wp[5];
+ s += x1[192] * w;
+ t -= x2[-192] * w;
+
+ /*
+ * this multiplyer could be removed, but it needs more 256 FLOAT data.
+ * thinking about the data cache performance, I think we should not
+ * use such a huge table. tt 2000/Oct/25
+ */
+ s *= wp[6];
+ w = t - s;
+ a[30 + i * 2] = t + s;
+ a[31 + i * 2] = wp[7] * w;
+ wp += 18;
+ x1--;
+ x2++;
+ }
+ {
+ FLOAT s, t, u, v;
+ t = x1[-16] * wp[-10];
+ s = x1[-32] * wp[-2];
+ t += (x1[-48] - x1[16]) * wp[-9];
+ s += x1[-96] * wp[-1];
+ t += (x1[-80] + x1[48]) * wp[-8];
+ s += x1[-160] * wp[0];
+ t += (x1[-112] - x1[80]) * wp[-7];
+ s += x1[-224] * wp[1];
+ t += (x1[-144] + x1[112]) * wp[-6];
+ s -= x1[32] * wp[2];
+ t += (x1[-176] - x1[144]) * wp[-5];
+ s -= x1[96] * wp[3];
+ t += (x1[-208] + x1[176]) * wp[-4];
+ s -= x1[160] * wp[4];
+ t += (x1[-240] - x1[208]) * wp[-3];
+ s -= x1[224];
+
+ u = s - t;
+ v = s + t;
+
+ t = a[14];
+ s = a[15] - t;
+
+ a[31] = v + t; /* A0 */
+ a[30] = u + s; /* A1 */
+ a[15] = u - s; /* A2 */
+ a[14] = v - t; /* A3 */
+ }
+ {
+ FLOAT xr;
+ xr = a[28] - a[0];
+ a[0] += a[28];
+ a[28] = xr * wp[-2 * 18 + 7];
+ xr = a[29] - a[1];
+ a[1] += a[29];
+ a[29] = xr * wp[-2 * 18 + 7];
+
+ xr = a[26] - a[2];
+ a[2] += a[26];
+ a[26] = xr * wp[-4 * 18 + 7];
+ xr = a[27] - a[3];
+ a[3] += a[27];
+ a[27] = xr * wp[-4 * 18 + 7];
+
+ xr = a[24] - a[4];
+ a[4] += a[24];
+ a[24] = xr * wp[-6 * 18 + 7];
+ xr = a[25] - a[5];
+ a[5] += a[25];
+ a[25] = xr * wp[-6 * 18 + 7];
+
+ xr = a[22] - a[6];
+ a[6] += a[22];
+ a[22] = xr * SQRT2;
+ xr = a[23] - a[7];
+ a[7] += a[23];
+ a[23] = xr * SQRT2 - a[7];
+ a[7] -= a[6];
+ a[22] -= a[7];
+ a[23] -= a[22];
+
+ xr = a[6];
+ a[6] = a[31] - xr;
+ a[31] = a[31] + xr;
+ xr = a[7];
+ a[7] = a[30] - xr;
+ a[30] = a[30] + xr;
+ xr = a[22];
+ a[22] = a[15] - xr;
+ a[15] = a[15] + xr;
+ xr = a[23];
+ a[23] = a[14] - xr;
+ a[14] = a[14] + xr;
+
+ xr = a[20] - a[8];
+ a[8] += a[20];
+ a[20] = xr * wp[-10 * 18 + 7];
+ xr = a[21] - a[9];
+ a[9] += a[21];
+ a[21] = xr * wp[-10 * 18 + 7];
+
+ xr = a[18] - a[10];
+ a[10] += a[18];
+ a[18] = xr * wp[-12 * 18 + 7];
+ xr = a[19] - a[11];
+ a[11] += a[19];
+ a[19] = xr * wp[-12 * 18 + 7];
+
+ xr = a[16] - a[12];
+ a[12] += a[16];
+ a[16] = xr * wp[-14 * 18 + 7];
+ xr = a[17] - a[13];
+ a[13] += a[17];
+ a[17] = xr * wp[-14 * 18 + 7];
+
+ xr = -a[20] + a[24];
+ a[20] += a[24];
+ a[24] = xr * wp[-12 * 18 + 7];
+ xr = -a[21] + a[25];
+ a[21] += a[25];
+ a[25] = xr * wp[-12 * 18 + 7];
+
+ xr = a[4] - a[8];
+ a[4] += a[8];
+ a[8] = xr * wp[-12 * 18 + 7];
+ xr = a[5] - a[9];
+ a[5] += a[9];
+ a[9] = xr * wp[-12 * 18 + 7];
+
+ xr = a[0] - a[12];
+ a[0] += a[12];
+ a[12] = xr * wp[-4 * 18 + 7];
+ xr = a[1] - a[13];
+ a[1] += a[13];
+ a[13] = xr * wp[-4 * 18 + 7];
+ xr = a[16] - a[28];
+ a[16] += a[28];
+ a[28] = xr * wp[-4 * 18 + 7];
+ xr = -a[17] + a[29];
+ a[17] += a[29];
+ a[29] = xr * wp[-4 * 18 + 7];
+
+ xr = SQRT2 * (a[2] - a[10]);
+ a[2] += a[10];
+ a[10] = xr;
+ xr = SQRT2 * (a[3] - a[11]);
+ a[3] += a[11];
+ a[11] = xr;
+ xr = SQRT2 * (-a[18] + a[26]);
+ a[18] += a[26];
+ a[26] = xr - a[18];
+ xr = SQRT2 * (-a[19] + a[27]);
+ a[19] += a[27];
+ a[27] = xr - a[19];
+
+ xr = a[2];
+ a[19] -= a[3];
+ a[3] -= xr;
+ a[2] = a[31] - xr;
+ a[31] += xr;
+ xr = a[3];
+ a[11] -= a[19];
+ a[18] -= xr;
+ a[3] = a[30] - xr;
+ a[30] += xr;
+ xr = a[18];
+ a[27] -= a[11];
+ a[19] -= xr;
+ a[18] = a[15] - xr;
+ a[15] += xr;
+
+ xr = a[19];
+ a[10] -= xr;
+ a[19] = a[14] - xr;
+ a[14] += xr;
+ xr = a[10];
+ a[11] -= xr;
+ a[10] = a[23] - xr;
+ a[23] += xr;
+ xr = a[11];
+ a[26] -= xr;
+ a[11] = a[22] - xr;
+ a[22] += xr;
+ xr = a[26];
+ a[27] -= xr;
+ a[26] = a[7] - xr;
+ a[7] += xr;
+
+ xr = a[27];
+ a[27] = a[6] - xr;
+ a[6] += xr;
+
+ xr = SQRT2 * (a[0] - a[4]);
+ a[0] += a[4];
+ a[4] = xr;
+ xr = SQRT2 * (a[1] - a[5]);
+ a[1] += a[5];
+ a[5] = xr;
+ xr = SQRT2 * (a[16] - a[20]);
+ a[16] += a[20];
+ a[20] = xr;
+ xr = SQRT2 * (a[17] - a[21]);
+ a[17] += a[21];
+ a[21] = xr;
+
+ xr = -SQRT2 * (a[8] - a[12]);
+ a[8] += a[12];
+ a[12] = xr - a[8];
+ xr = -SQRT2 * (a[9] - a[13]);
+ a[9] += a[13];
+ a[13] = xr - a[9];
+ xr = -SQRT2 * (a[25] - a[29]);
+ a[25] += a[29];
+ a[29] = xr - a[25];
+ xr = -SQRT2 * (a[24] + a[28]);
+ a[24] -= a[28];
+ a[28] = xr - a[24];
+
+ xr = a[24] - a[16];
+ a[24] = xr;
+ xr = a[20] - xr;
+ a[20] = xr;
+ xr = a[28] - xr;
+ a[28] = xr;
+
+ xr = a[25] - a[17];
+ a[25] = xr;
+ xr = a[21] - xr;
+ a[21] = xr;
+ xr = a[29] - xr;
+ a[29] = xr;
+
+ xr = a[17] - a[1];
+ a[17] = xr;
+ xr = a[9] - xr;
+ a[9] = xr;
+ xr = a[25] - xr;
+ a[25] = xr;
+ xr = a[5] - xr;
+ a[5] = xr;
+ xr = a[21] - xr;
+ a[21] = xr;
+ xr = a[13] - xr;
+ a[13] = xr;
+ xr = a[29] - xr;
+ a[29] = xr;
+
+ xr = a[1] - a[0];
+ a[1] = xr;
+ xr = a[16] - xr;
+ a[16] = xr;
+ xr = a[17] - xr;
+ a[17] = xr;
+ xr = a[8] - xr;
+ a[8] = xr;
+ xr = a[9] - xr;
+ a[9] = xr;
+ xr = a[24] - xr;
+ a[24] = xr;
+ xr = a[25] - xr;
+ a[25] = xr;
+ xr = a[4] - xr;
+ a[4] = xr;
+ xr = a[5] - xr;
+ a[5] = xr;
+ xr = a[20] - xr;
+ a[20] = xr;
+ xr = a[21] - xr;
+ a[21] = xr;
+ xr = a[12] - xr;
+ a[12] = xr;
+ xr = a[13] - xr;
+ a[13] = xr;
+ xr = a[28] - xr;
+ a[28] = xr;
+ xr = a[29] - xr;
+ a[29] = xr;
+
+ xr = a[0];
+ a[0] += a[31];
+ a[31] -= xr;
+ xr = a[1];
+ a[1] += a[30];
+ a[30] -= xr;
+ xr = a[16];
+ a[16] += a[15];
+ a[15] -= xr;
+ xr = a[17];
+ a[17] += a[14];
+ a[14] -= xr;
+ xr = a[8];
+ a[8] += a[23];
+ a[23] -= xr;
+ xr = a[9];
+ a[9] += a[22];
+ a[22] -= xr;
+ xr = a[24];
+ a[24] += a[7];
+ a[7] -= xr;
+ xr = a[25];
+ a[25] += a[6];
+ a[6] -= xr;
+ xr = a[4];
+ a[4] += a[27];
+ a[27] -= xr;
+ xr = a[5];
+ a[5] += a[26];
+ a[26] -= xr;
+ xr = a[20];
+ a[20] += a[11];
+ a[11] -= xr;
+ xr = a[21];
+ a[21] += a[10];
+ a[10] -= xr;
+ xr = a[12];
+ a[12] += a[19];
+ a[19] -= xr;
+ xr = a[13];
+ a[13] += a[18];
+ a[18] -= xr;
+ xr = a[28];
+ a[28] += a[3];
+ a[3] -= xr;
+ xr = a[29];
+ a[29] += a[2];
+ a[2] -= xr;
+ }
+
+}
+
+
+/*-------------------------------------------------------------------*/
+/* */
+/* Function: Calculation of the MDCT */
+/* In the case of long blocks (type 0,1,3) there are */
+/* 36 coefficents in the time domain and 18 in the frequency */
+/* domain. */
+/* In the case of short blocks (type 2) there are 3 */
+/* transformations with short length. This leads to 12 coefficents */
+/* in the time and 6 in the frequency domain. In this case the */
+/* results are stored side by side in the vector out[]. */
+/* */
+/* New layer3 */
+/* */
+/*-------------------------------------------------------------------*/
+
+inline static void
+mdct_short(FLOAT * inout)
+{
+ int l;
+ for (l = 0; l < 3; l++) {
+ FLOAT tc0, tc1, tc2, ts0, ts1, ts2;
+
+ ts0 = inout[2 * 3] * win[SHORT_TYPE][0] - inout[5 * 3];
+ tc0 = inout[0 * 3] * win[SHORT_TYPE][2] - inout[3 * 3];
+ tc1 = ts0 + tc0;
+ tc2 = ts0 - tc0;
+
+ ts0 = inout[5 * 3] * win[SHORT_TYPE][0] + inout[2 * 3];
+ tc0 = inout[3 * 3] * win[SHORT_TYPE][2] + inout[0 * 3];
+ ts1 = ts0 + tc0;
+ ts2 = -ts0 + tc0;
+
+ tc0 = (inout[1 * 3] * win[SHORT_TYPE][1] - inout[4 * 3]) * 2.069978111953089e-11; /* tritab_s[1] */
+ ts0 = (inout[4 * 3] * win[SHORT_TYPE][1] + inout[1 * 3]) * 2.069978111953089e-11; /* tritab_s[1] */
+
+ inout[3 * 0] = tc1 * 1.907525191737280e-11 /* tritab_s[2] */ + tc0;
+ inout[3 * 5] = -ts1 * 1.907525191737280e-11 /* tritab_s[0] */ + ts0;
+
+ tc2 = tc2 * 0.86602540378443870761 * 1.907525191737281e-11 /* tritab_s[2] */ ;
+ ts1 = ts1 * 0.5 * 1.907525191737281e-11 + ts0;
+ inout[3 * 1] = tc2 - ts1;
+ inout[3 * 2] = tc2 + ts1;
+
+ tc1 = tc1 * 0.5 * 1.907525191737281e-11 - tc0;
+ ts2 = ts2 * 0.86602540378443870761 * 1.907525191737281e-11 /* tritab_s[0] */ ;
+ inout[3 * 3] = tc1 + ts2;
+ inout[3 * 4] = tc1 - ts2;
+
+ inout++;
+ }
+}
+
+inline static void
+mdct_long(FLOAT * out, FLOAT const *in)
+{
+ FLOAT ct, st;
+ {
+ FLOAT tc1, tc2, tc3, tc4, ts5, ts6, ts7, ts8;
+ /* 1,2, 5,6, 9,10, 13,14, 17 */
+ tc1 = in[17] - in[9];
+ tc3 = in[15] - in[11];
+ tc4 = in[14] - in[12];
+ ts5 = in[0] + in[8];
+ ts6 = in[1] + in[7];
+ ts7 = in[2] + in[6];
+ ts8 = in[3] + in[5];
+
+ out[17] = (ts5 + ts7 - ts8) - (ts6 - in[4]);
+ st = (ts5 + ts7 - ts8) * cx[7] + (ts6 - in[4]);
+ ct = (tc1 - tc3 - tc4) * cx[6];
+ out[5] = ct + st;
+ out[6] = ct - st;
+
+ tc2 = (in[16] - in[10]) * cx[6];
+ ts6 = ts6 * cx[7] + in[4];
+ ct = tc1 * cx[0] + tc2 + tc3 * cx[1] + tc4 * cx[2];
+ st = -ts5 * cx[4] + ts6 - ts7 * cx[5] + ts8 * cx[3];
+ out[1] = ct + st;
+ out[2] = ct - st;
+
+ ct = tc1 * cx[1] - tc2 - tc3 * cx[2] + tc4 * cx[0];
+ st = -ts5 * cx[5] + ts6 - ts7 * cx[3] + ts8 * cx[4];
+ out[9] = ct + st;
+ out[10] = ct - st;
+
+ ct = tc1 * cx[2] - tc2 + tc3 * cx[0] - tc4 * cx[1];
+ st = ts5 * cx[3] - ts6 + ts7 * cx[4] - ts8 * cx[5];
+ out[13] = ct + st;
+ out[14] = ct - st;
+ }
+ {
+ FLOAT ts1, ts2, ts3, ts4, tc5, tc6, tc7, tc8;
+
+ ts1 = in[8] - in[0];
+ ts3 = in[6] - in[2];
+ ts4 = in[5] - in[3];
+ tc5 = in[17] + in[9];
+ tc6 = in[16] + in[10];
+ tc7 = in[15] + in[11];
+ tc8 = in[14] + in[12];
+
+ out[0] = (tc5 + tc7 + tc8) + (tc6 + in[13]);
+ ct = (tc5 + tc7 + tc8) * cx[7] - (tc6 + in[13]);
+ st = (ts1 - ts3 + ts4) * cx[6];
+ out[11] = ct + st;
+ out[12] = ct - st;
+
+ ts2 = (in[7] - in[1]) * cx[6];
+ tc6 = in[13] - tc6 * cx[7];
+ ct = tc5 * cx[3] - tc6 + tc7 * cx[4] + tc8 * cx[5];
+ st = ts1 * cx[2] + ts2 + ts3 * cx[0] + ts4 * cx[1];
+ out[3] = ct + st;
+ out[4] = ct - st;
+
+ ct = -tc5 * cx[5] + tc6 - tc7 * cx[3] - tc8 * cx[4];
+ st = ts1 * cx[1] + ts2 - ts3 * cx[2] - ts4 * cx[0];
+ out[7] = ct + st;
+ out[8] = ct - st;
+
+ ct = -tc5 * cx[4] + tc6 - tc7 * cx[5] - tc8 * cx[3];
+ st = ts1 * cx[0] - ts2 + ts3 * cx[1] - ts4 * cx[2];
+ out[15] = ct + st;
+ out[16] = ct - st;
+ }
+}
+
+
+void
+mdct_sub48(lame_internal_flags * gfc, const sample_t * w0, const sample_t * w1)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int gr, k, ch;
+ const sample_t *wk;
+
+ wk = w0 + 286;
+ /* thinking cache performance, ch->gr loop is better than gr->ch loop */
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ int band;
+ gr_info *const gi = &(gfc->l3_side.tt[gr][ch]);
+ FLOAT *mdct_enc = gi->xr;
+ FLOAT *samp = esv->sb_sample[ch][1 - gr][0];
+
+ for (k = 0; k < 18 / 2; k++) {
+ window_subband(wk, samp);
+ window_subband(wk + 32, samp + 32);
+ samp += 64;
+ wk += 64;
+ /*
+ * Compensate for inversion in the analysis filter
+ */
+ for (band = 1; band < 32; band += 2) {
+ samp[band - 32] *= -1;
+ }
+ }
+
+ /*
+ * Perform imdct of 18 previous subband samples
+ * + 18 current subband samples
+ */
+ for (band = 0; band < 32; band++, mdct_enc += 18) {
+ int type = gi->block_type;
+ FLOAT const *const band0 = esv->sb_sample[ch][gr][0] + order[band];
+ FLOAT *const band1 = esv->sb_sample[ch][1 - gr][0] + order[band];
+ if (gi->mixed_block_flag && band < 2)
+ type = 0;
+ if (esv->amp_filter[band] < 1e-12) {
+ memset(mdct_enc, 0, 18 * sizeof(FLOAT));
+ }
+ else {
+ if (esv->amp_filter[band] < 1.0) {
+ for (k = 0; k < 18; k++)
+ band1[k * 32] *= esv->amp_filter[band];
+ }
+ if (type == SHORT_TYPE) {
+ for (k = -NS / 4; k < 0; k++) {
+ FLOAT const w = win[SHORT_TYPE][k + 3];
+ mdct_enc[k * 3 + 9] = band0[(9 + k) * 32] * w - band0[(8 - k) * 32];
+ mdct_enc[k * 3 + 18] = band0[(14 - k) * 32] * w + band0[(15 + k) * 32];
+ mdct_enc[k * 3 + 10] = band0[(15 + k) * 32] * w - band0[(14 - k) * 32];
+ mdct_enc[k * 3 + 19] = band1[(2 - k) * 32] * w + band1[(3 + k) * 32];
+ mdct_enc[k * 3 + 11] = band1[(3 + k) * 32] * w - band1[(2 - k) * 32];
+ mdct_enc[k * 3 + 20] = band1[(8 - k) * 32] * w + band1[(9 + k) * 32];
+ }
+ mdct_short(mdct_enc);
+ }
+ else {
+ FLOAT work[18];
+ for (k = -NL / 4; k < 0; k++) {
+ FLOAT a, b;
+ a = win[type][k + 27] * band1[(k + 9) * 32]
+ + win[type][k + 36] * band1[(8 - k) * 32];
+ b = win[type][k + 9] * band0[(k + 9) * 32]
+ - win[type][k + 18] * band0[(8 - k) * 32];
+ work[k + 9] = a - b * tantab_l[k + 9];
+ work[k + 18] = a * tantab_l[k + 9] + b;
+ }
+
+ mdct_long(mdct_enc, work);
+ }
+ }
+ /*
+ * Perform aliasing reduction butterfly
+ */
+ if (type != SHORT_TYPE && band != 0) {
+ for (k = 7; k >= 0; --k) {
+ FLOAT bu, bd;
+ bu = mdct_enc[k] * ca[k] + mdct_enc[-1 - k] * cs[k];
+ bd = mdct_enc[k] * cs[k] - mdct_enc[-1 - k] * ca[k];
+
+ mdct_enc[-1 - k] = bu;
+ mdct_enc[k] = bd;
+ }
+ }
+ }
+ }
+ wk = w1 + 286;
+ if (cfg->mode_gr == 1) {
+ memcpy(esv->sb_sample[ch][0], esv->sb_sample[ch][1], 576 * sizeof(FLOAT));
+ }
+ }
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/newmdct.h b/libnative/src/main/cpp/module/mp3/lame/newmdct.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b58a954c883bfd763d95b9359c4bcd9f20638d0
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/newmdct.h
@@ -0,0 +1,27 @@
+/*
+ * New Modified DCT include file
+ *
+ * Copyright (c) 1999 Takehiro TOMINAGA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_NEWMDCT_H
+#define LAME_NEWMDCT_H
+
+void mdct_sub48(lame_internal_flags * gfc, const sample_t * w0, const sample_t * w1);
+
+#endif /* LAME_NEWMDCT_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/presets.c b/libnative/src/main/cpp/module/mp3/lame/presets.c
new file mode 100644
index 0000000000000000000000000000000000000000..16f3a94f8aa7e96a72327363883557f0b0f58e1b
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/presets.c
@@ -0,0 +1,400 @@
+/*
+ * presets.c -- Apply presets
+ *
+ * Copyright (c) 2002-2008 Gabriel Bouvigne
+ * Copyright (c) 2007-2011 Robert Hegemann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "set_get.h"
+#include "encoder.h"
+#include "util.h"
+#include "lame_global_flags.h"
+
+#define SET_OPTION(opt, val, def) if (enforce) \
+ (void) lame_set_##opt(gfp, val); \
+ else if (!(fabs(lame_get_##opt(gfp) - def) > 0)) \
+ (void) lame_set_##opt(gfp, val);
+
+#define SET__OPTION(opt, val, def) if (enforce) \
+ lame_set_##opt(gfp, val); \
+ else if (!(fabs(lame_get_##opt(gfp) - def) > 0)) \
+ lame_set_##opt(gfp, val);
+
+#undef Min
+#undef Max
+
+static inline int
+min_int(int a, int b)
+{
+ if (a < b) {
+ return a;
+ }
+ return b;
+}
+
+static inline int
+max_int(int a, int b)
+{
+ if (a > b) {
+ return a;
+ }
+ return b;
+}
+
+
+
+typedef struct {
+ int vbr_q;
+ int quant_comp;
+ int quant_comp_s;
+ int expY;
+ FLOAT st_lrm; /*short threshold */
+ FLOAT st_s;
+ FLOAT masking_adj;
+ FLOAT masking_adj_short;
+ FLOAT ath_lower;
+ FLOAT ath_curve;
+ FLOAT ath_sensitivity;
+ FLOAT interch;
+ int safejoint;
+ int sfb21mod;
+ FLOAT msfix;
+ FLOAT minval;
+ FLOAT ath_fixpoint;
+} vbr_presets_t;
+
+ /* *INDENT-OFF* */
+
+ /* Switch mappings for VBR mode VBR_RH */
+ static const vbr_presets_t vbr_old_switch_map[] = {
+ /*vbr_q qcomp_l qcomp_s expY st_lrm st_s mask adj_l adj_s ath_lower ath_curve ath_sens interChR safejoint sfb21mod msfix */
+ {0, 9, 9, 0, 5.20, 125.0, -4.2, -6.3, 4.8, 1, 0, 0, 2, 21, 0.97, 5, 100},
+ {1, 9, 9, 0, 5.30, 125.0, -3.6, -5.6, 4.5, 1.5, 0, 0, 2, 21, 1.35, 5, 100},
+ {2, 9, 9, 0, 5.60, 125.0, -2.2, -3.5, 2.8, 2, 0, 0, 2, 21, 1.49, 5, 100},
+ {3, 9, 9, 1, 5.80, 130.0, -1.8, -2.8, 2.6, 3, -4, 0, 2, 20, 1.64, 5, 100},
+ {4, 9, 9, 1, 6.00, 135.0, -0.7, -1.1, 1.1, 3.5, -8, 0, 2, 0, 1.79, 5, 100},
+ {5, 9, 9, 1, 6.40, 140.0, 0.5, 0.4, -7.5, 4, -12, 0.0002, 0, 0, 1.95, 5, 100},
+ {6, 9, 9, 1, 6.60, 145.0, 0.67, 0.65, -14.7, 6.5, -19, 0.0004, 0, 0, 2.30, 5, 100},
+ {7, 9, 9, 1, 6.60, 145.0, 0.8, 0.75, -19.7, 8, -22, 0.0006, 0, 0, 2.70, 5, 100},
+ {8, 9, 9, 1, 6.60, 145.0, 1.2, 1.15, -27.5, 10, -23, 0.0007, 0, 0, 0, 5, 100},
+ {9, 9, 9, 1, 6.60, 145.0, 1.6, 1.6, -36, 11, -25, 0.0008, 0, 0, 0, 5, 100},
+ {10, 9, 9, 1, 6.60, 145.0, 2.0, 2.0, -36, 12, -25, 0.0008, 0, 0, 0, 5, 100}
+ };
+
+ static const vbr_presets_t vbr_mt_psy_switch_map[] = {
+ /*vbr_q qcomp_l qcomp_s expY st_lrm st_s mask adj_l adj_s ath_lower ath_curve ath_sens --- safejoint sfb21mod msfix */
+ {0, 9, 9, 0, 4.20, 25.0, -6.8, -6.8, 7.1, 1, 0, 0, 2, 31, 1.000, 5, 100},
+ {1, 9, 9, 0, 4.20, 25.0, -4.8, -4.8, 5.4, 1.4, -1, 0, 2, 27, 1.122, 5, 98},
+ {2, 9, 9, 0, 4.20, 25.0, -2.6, -2.6, 3.7, 2.0, -3, 0, 2, 23, 1.288, 5, 97},
+ {3, 9, 9, 1, 4.20, 25.0, -1.6, -1.6, 2.0, 2.0, -5, 0, 2, 18, 1.479, 5, 96},
+ {4, 9, 9, 1, 4.20, 25.0, -0.0, -0.0, 0.0, 2.0, -8, 0, 2, 12, 1.698, 5, 95},
+ {5, 9, 9, 1, 4.20, 25.0, 1.3, 1.3, -6, 3.5, -11, 0, 2, 8, 1.950, 5, 94.2},
+#if 0
+ {6, 9, 9, 1, 4.50, 100.0, 1.5, 1.5, -24.0, 6.0, -14, 0, 2, 4, 2.239, 3, 93.9},
+ {7, 9, 9, 1, 4.80, 200.0, 1.7, 1.7, -28.0, 9.0, -20, 0, 2, 0, 2.570, 1, 93.6},
+#else
+ {6, 9, 9, 1, 4.50, 100.0, 2.2, 2.3, -12.0, 6.0, -14, 0, 2, 4, 2.239, 3, 93.9},
+ {7, 9, 9, 1, 4.80, 200.0, 2.7, 2.7, -18.0, 9.0, -17, 0, 2, 0, 2.570, 1, 93.6},
+#endif
+ {8, 9, 9, 1, 5.30, 300.0, 2.8, 2.8, -21.0, 10.0, -23, 0.0002, 0, 0, 2.951, 0, 93.3},
+ {9, 9, 9, 1, 6.60, 300.0, 2.8, 2.8, -23.0, 11.0, -25, 0.0006, 0, 0, 3.388, 0, 93.3},
+ {10, 9, 9, 1, 25.00, 300.0, 2.8, 2.8, -25.0, 12.0, -27, 0.0025, 0, 0, 3.500, 0, 93.3}
+ };
+
+ /* *INDENT-ON* */
+
+static vbr_presets_t const*
+get_vbr_preset(int v)
+{
+ switch (v) {
+ case vbr_mtrh:
+ case vbr_mt:
+ return &vbr_mt_psy_switch_map[0];
+ default:
+ return &vbr_old_switch_map[0];
+ }
+}
+
+#define NOOP(m) (void)p.m
+#define LERP(m) (p.m = p.m + x * (q.m - p.m))
+
+static void
+apply_vbr_preset(lame_global_flags * gfp, int a, int enforce)
+{
+ vbr_presets_t const *vbr_preset = get_vbr_preset(lame_get_VBR(gfp));
+ float x = gfp->VBR_q_frac;
+ vbr_presets_t p = vbr_preset[a];
+ vbr_presets_t q = vbr_preset[a + 1];
+ vbr_presets_t const *set = &p;
+
+ NOOP(vbr_q);
+ NOOP(quant_comp);
+ NOOP(quant_comp_s);
+ NOOP(expY);
+ LERP(st_lrm);
+ LERP(st_s);
+ LERP(masking_adj);
+ LERP(masking_adj_short);
+ LERP(ath_lower);
+ LERP(ath_curve);
+ LERP(ath_sensitivity);
+ LERP(interch);
+ NOOP(safejoint);
+ LERP(sfb21mod);
+ LERP(msfix);
+ LERP(minval);
+ LERP(ath_fixpoint);
+
+ (void) lame_set_VBR_q(gfp, set->vbr_q);
+ SET_OPTION(quant_comp, set->quant_comp, -1);
+ SET_OPTION(quant_comp_short, set->quant_comp_s, -1);
+ if (set->expY) {
+ (void) lame_set_experimentalY(gfp, set->expY);
+ }
+ SET_OPTION(short_threshold_lrm, set->st_lrm, -1);
+ SET_OPTION(short_threshold_s, set->st_s, -1);
+ SET_OPTION(maskingadjust, set->masking_adj, 0);
+ SET_OPTION(maskingadjust_short, set->masking_adj_short, 0);
+ if (lame_get_VBR(gfp) == vbr_mt || lame_get_VBR(gfp) == vbr_mtrh) {
+ lame_set_ATHtype(gfp, 5);
+ }
+ SET_OPTION(ATHlower, set->ath_lower, 0);
+ SET_OPTION(ATHcurve, set->ath_curve, -1);
+ SET_OPTION(athaa_sensitivity, set->ath_sensitivity, 0);
+ if (set->interch > 0) {
+ SET_OPTION(interChRatio, set->interch, -1);
+ }
+
+ /* parameters for which there is no proper set/get interface */
+ if (set->safejoint > 0) {
+ (void) lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | 2);
+ }
+ if (set->sfb21mod > 0) {
+ int const nsp = lame_get_exp_nspsytune(gfp);
+ int const val = (nsp >> 20) & 63;
+ if (val == 0) {
+ int const sf21mod = (set->sfb21mod << 20) | nsp;
+ (void) lame_set_exp_nspsytune(gfp, sf21mod);
+ }
+ }
+ SET__OPTION(msfix, set->msfix, -1);
+
+ if (enforce == 0) {
+ gfp->VBR_q = a;
+ gfp->VBR_q_frac = x;
+ }
+ gfp->internal_flags->cfg.minval = set->minval;
+ gfp->internal_flags->cfg.ATHfixpoint = set->ath_fixpoint;
+}
+
+static int
+apply_abr_preset(lame_global_flags * gfp, int preset, int enforce)
+{
+ typedef struct {
+ int abr_kbps;
+ int quant_comp;
+ int quant_comp_s;
+ int safejoint;
+ FLOAT nsmsfix;
+ FLOAT st_lrm; /*short threshold */
+ FLOAT st_s;
+ FLOAT scale;
+ FLOAT masking_adj;
+ FLOAT ath_lower;
+ FLOAT ath_curve;
+ FLOAT interch;
+ int sfscale;
+ } abr_presets_t;
+
+
+ /* *INDENT-OFF* */
+
+ /*
+ * Switch mappings for ABR mode
+ */
+ const abr_presets_t abr_switch_map[] = {
+ /* kbps quant q_s safejoint nsmsfix st_lrm st_s scale msk ath_lwr ath_curve interch , sfscale */
+ { 8, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -30.0, 11, 0.0012, 1}, /* 8, impossible to use in stereo */
+ { 16, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -25.0, 11, 0.0010, 1}, /* 16 */
+ { 24, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -20.0, 11, 0.0010, 1}, /* 24 */
+ { 32, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -15.0, 11, 0.0010, 1}, /* 32 */
+ { 40, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -10.0, 11, 0.0009, 1}, /* 40 */
+ { 48, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -10.0, 11, 0.0009, 1}, /* 48 */
+ { 56, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -6.0, 11, 0.0008, 1}, /* 56 */
+ { 64, 9, 9, 0, 0, 6.60, 145, 0.95, 0, -2.0, 11, 0.0008, 1}, /* 64 */
+ { 80, 9, 9, 0, 0, 6.60, 145, 0.95, 0, .0, 8, 0.0007, 1}, /* 80 */
+ { 96, 9, 9, 0, 2.50, 6.60, 145, 0.95, 0, 1.0, 5.5, 0.0006, 1}, /* 96 */
+ {112, 9, 9, 0, 2.25, 6.60, 145, 0.95, 0, 2.0, 4.5, 0.0005, 1}, /* 112 */
+ {128, 9, 9, 0, 1.95, 6.40, 140, 0.95, 0, 3.0, 4, 0.0002, 1}, /* 128 */
+ {160, 9, 9, 1, 1.79, 6.00, 135, 0.95, -2, 5.0, 3.5, 0, 1}, /* 160 */
+ {192, 9, 9, 1, 1.49, 5.60, 125, 0.97, -4, 7.0, 3, 0, 0}, /* 192 */
+ {224, 9, 9, 1, 1.25, 5.20, 125, 0.98, -6, 9.0, 2, 0, 0}, /* 224 */
+ {256, 9, 9, 1, 0.97, 5.20, 125, 1.00, -8, 10.0, 1, 0, 0}, /* 256 */
+ {320, 9, 9, 1, 0.90, 5.20, 125, 1.00, -10, 12.0, 0, 0, 0} /* 320 */
+ };
+
+ /* *INDENT-ON* */
+
+ /* Variables for the ABR stuff */
+ int r;
+ int actual_bitrate = preset;
+
+ r = nearestBitrateFullIndex(preset);
+
+ (void) lame_set_VBR(gfp, vbr_abr);
+ (void) lame_set_VBR_mean_bitrate_kbps(gfp, (actual_bitrate));
+ (void) lame_set_VBR_mean_bitrate_kbps(gfp, min_int(lame_get_VBR_mean_bitrate_kbps(gfp), 320));
+ (void) lame_set_VBR_mean_bitrate_kbps(gfp, max_int(lame_get_VBR_mean_bitrate_kbps(gfp), 8));
+ (void) lame_set_brate(gfp, lame_get_VBR_mean_bitrate_kbps(gfp));
+
+
+ /* parameters for which there is no proper set/get interface */
+ if (abr_switch_map[r].safejoint > 0)
+ (void) lame_set_exp_nspsytune(gfp, lame_get_exp_nspsytune(gfp) | 2); /* safejoint */
+
+ if (abr_switch_map[r].sfscale > 0)
+ (void) lame_set_sfscale(gfp, 1);
+
+
+ SET_OPTION(quant_comp, abr_switch_map[r].quant_comp, -1);
+ SET_OPTION(quant_comp_short, abr_switch_map[r].quant_comp_s, -1);
+
+ SET__OPTION(msfix, abr_switch_map[r].nsmsfix, -1);
+
+ SET_OPTION(short_threshold_lrm, abr_switch_map[r].st_lrm, -1);
+ SET_OPTION(short_threshold_s, abr_switch_map[r].st_s, -1);
+
+ /* ABR seems to have big problems with clipping, especially at low bitrates */
+ /* so we compensate for that here by using a scale value depending on bitrate */
+ lame_set_scale(gfp, lame_get_scale(gfp) * abr_switch_map[r].scale);
+
+ SET_OPTION(maskingadjust, abr_switch_map[r].masking_adj, 0);
+ if (abr_switch_map[r].masking_adj > 0) {
+ SET_OPTION(maskingadjust_short, abr_switch_map[r].masking_adj * .9, 0);
+ }
+ else {
+ SET_OPTION(maskingadjust_short, abr_switch_map[r].masking_adj * 1.1, 0);
+ }
+
+
+ SET_OPTION(ATHlower, abr_switch_map[r].ath_lower, 0);
+ SET_OPTION(ATHcurve, abr_switch_map[r].ath_curve, -1);
+
+ SET_OPTION(interChRatio, abr_switch_map[r].interch, -1);
+
+ (void) abr_switch_map[r].abr_kbps;
+
+ gfp->internal_flags->cfg.minval = 5. * (abr_switch_map[r].abr_kbps / 320.);
+
+ return preset;
+}
+
+
+
+int
+apply_preset(lame_global_flags * gfp, int preset, int enforce)
+{
+ /*translate legacy presets */
+ switch (preset) {
+ case R3MIX:
+ {
+ preset = V3;
+ (void) lame_set_VBR(gfp, vbr_mtrh);
+ break;
+ }
+ case MEDIUM:
+ case MEDIUM_FAST:
+ {
+ preset = V4;
+ (void) lame_set_VBR(gfp, vbr_mtrh);
+ break;
+ }
+ case STANDARD:
+ case STANDARD_FAST:
+ {
+ preset = V2;
+ (void) lame_set_VBR(gfp, vbr_mtrh);
+ break;
+ }
+ case EXTREME:
+ case EXTREME_FAST:
+ {
+ preset = V0;
+ (void) lame_set_VBR(gfp, vbr_mtrh);
+ break;
+ }
+ case INSANE:
+ {
+ preset = 320;
+ gfp->preset = preset;
+ (void) apply_abr_preset(gfp, preset, enforce);
+ lame_set_VBR(gfp, vbr_off);
+ return preset;
+ }
+ }
+
+ gfp->preset = preset;
+ {
+ switch (preset) {
+ case V9:
+ apply_vbr_preset(gfp, 9, enforce);
+ return preset;
+ case V8:
+ apply_vbr_preset(gfp, 8, enforce);
+ return preset;
+ case V7:
+ apply_vbr_preset(gfp, 7, enforce);
+ return preset;
+ case V6:
+ apply_vbr_preset(gfp, 6, enforce);
+ return preset;
+ case V5:
+ apply_vbr_preset(gfp, 5, enforce);
+ return preset;
+ case V4:
+ apply_vbr_preset(gfp, 4, enforce);
+ return preset;
+ case V3:
+ apply_vbr_preset(gfp, 3, enforce);
+ return preset;
+ case V2:
+ apply_vbr_preset(gfp, 2, enforce);
+ return preset;
+ case V1:
+ apply_vbr_preset(gfp, 1, enforce);
+ return preset;
+ case V0:
+ apply_vbr_preset(gfp, 0, enforce);
+ return preset;
+ default:
+ break;
+ }
+ }
+ if (8 <= preset && preset <= 320) {
+ return apply_abr_preset(gfp, preset, enforce);
+ }
+
+ gfp->preset = 0; /*no corresponding preset found */
+ return preset;
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/psymodel.c b/libnative/src/main/cpp/module/mp3/lame/psymodel.c
new file mode 100644
index 0000000000000000000000000000000000000000..a224bcce58aee7653f2f4d2c9c02bb42337f8243
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/psymodel.c
@@ -0,0 +1,2158 @@
+/*
+ * psymodel.c
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 2001-2002 Naoki Shibata
+ * Copyright (c) 2000-2003 Takehiro Tominaga
+ * Copyright (c) 2000-2011 Robert Hegemann
+ * Copyright (c) 2000-2005 Gabriel Bouvigne
+ * Copyright (c) 2000-2005 Alexander Leidinger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: psymodel.c,v 1.209 2011/05/24 20:45:55 robert Exp $ */
+
+
+/*
+PSYCHO ACOUSTICS
+
+
+This routine computes the psycho acoustics, delayed by one granule.
+
+Input: buffer of PCM data (1024 samples).
+
+This window should be centered over the 576 sample granule window.
+The routine will compute the psycho acoustics for
+this granule, but return the psycho acoustics computed
+for the *previous* granule. This is because the block
+type of the previous granule can only be determined
+after we have computed the psycho acoustics for the following
+granule.
+
+Output: maskings and energies for each scalefactor band.
+block type, PE, and some correlation measures.
+The PE is used by CBR modes to determine if extra bits
+from the bit reservoir should be used. The correlation
+measures are used to determine mid/side or regular stereo.
+*/
+/*
+Notation:
+
+barks: a non-linear frequency scale. Mapping from frequency to
+ barks is given by freq2bark()
+
+scalefactor bands: The spectrum (frequencies) are broken into
+ SBMAX "scalefactor bands". Thes bands
+ are determined by the MPEG ISO spec. In
+ the noise shaping/quantization code, we allocate
+ bits among the partition bands to achieve the
+ best possible quality
+
+partition bands: The spectrum is also broken into about
+ 64 "partition bands". Each partition
+ band is about .34 barks wide. There are about 2-5
+ partition bands for each scalefactor band.
+
+LAME computes all psycho acoustic information for each partition
+band. Then at the end of the computations, this information
+is mapped to scalefactor bands. The energy in each scalefactor
+band is taken as the sum of the energy in all partition bands
+which overlap the scalefactor band. The maskings can be computed
+in the same way (and thus represent the average masking in that band)
+or by taking the minmum value multiplied by the number of
+partition bands used (which represents a minimum masking in that band).
+*/
+/*
+The general outline is as follows:
+
+1. compute the energy in each partition band
+2. compute the tonality in each partition band
+3. compute the strength of each partion band "masker"
+4. compute the masking (via the spreading function applied to each masker)
+5. Modifications for mid/side masking.
+
+Each partition band is considiered a "masker". The strength
+of the i'th masker in band j is given by:
+
+ s3(bark(i)-bark(j))*strength(i)
+
+The strength of the masker is a function of the energy and tonality.
+The more tonal, the less masking. LAME uses a simple linear formula
+(controlled by NMT and TMN) which says the strength is given by the
+energy divided by a linear function of the tonality.
+*/
+/*
+s3() is the "spreading function". It is given by a formula
+determined via listening tests.
+
+The total masking in the j'th partition band is the sum over
+all maskings i. It is thus given by the convolution of
+the strength with s3(), the "spreading function."
+
+masking(j) = sum_over_i s3(i-j)*strength(i) = s3 o strength
+
+where "o" = convolution operator. s3 is given by a formula determined
+via listening tests. It is normalized so that s3 o 1 = 1.
+
+Note: instead of a simple convolution, LAME also has the
+option of using "additive masking"
+
+The most critical part is step 2, computing the tonality of each
+partition band. LAME has two tonality estimators. The first
+is based on the ISO spec, and measures how predictiable the
+signal is over time. The more predictable, the more tonal.
+The second measure is based on looking at the spectrum of
+a single granule. The more peaky the spectrum, the more
+tonal. By most indications, the latter approach is better.
+
+Finally, in step 5, the maskings for the mid and side
+channel are possibly increased. Under certain circumstances,
+noise in the mid & side channels is assumed to also
+be masked by strong maskers in the L or R channels.
+
+
+Other data computed by the psy-model:
+
+ms_ratio side-channel / mid-channel masking ratio (for previous granule)
+ms_ratio_next side-channel / mid-channel masking ratio for this granule
+
+percep_entropy[2] L and R values (prev granule) of PE - A measure of how
+ much pre-echo is in the previous granule
+percep_entropy_MS[2] mid and side channel values (prev granule) of percep_entropy
+energy[4] L,R,M,S energy in each channel, prev granule
+blocktype_d[2] block type to use for previous granule
+*/
+
+
+
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "psymodel.h"
+#include "lame_global_flags.h"
+#include "fft.h"
+#include "lame-analysis.h"
+
+
+#define NSFIRLEN 21
+
+#ifdef M_LN10
+#define LN_TO_LOG10 (M_LN10/10)
+#else
+#define LN_TO_LOG10 0.2302585093
+#endif
+
+
+/*
+ L3psycho_anal. Compute psycho acoustics.
+
+ Data returned to the calling program must be delayed by one
+ granule.
+
+ This is done in two places.
+ If we do not need to know the blocktype, the copying
+ can be done here at the top of the program: we copy the data for
+ the last granule (computed during the last call) before it is
+ overwritten with the new data. It looks like this:
+
+ 0. static psymodel_data
+ 1. calling_program_data = psymodel_data
+ 2. compute psymodel_data
+
+ For data which needs to know the blocktype, the copying must be
+ done at the end of this loop, and the old values must be saved:
+
+ 0. static psymodel_data_old
+ 1. compute psymodel_data
+ 2. compute possible block type of this granule
+ 3. compute final block type of previous granule based on #2.
+ 4. calling_program_data = psymodel_data_old
+ 5. psymodel_data_old = psymodel_data
+*/
+
+
+
+
+
+/* psycho_loudness_approx
+ jd - 2001 mar 12
+in: energy - BLKSIZE/2 elements of frequency magnitudes ^ 2
+ gfp - uses out_samplerate, ATHtype (also needed for ATHformula)
+returns: loudness^2 approximation, a positive value roughly tuned for a value
+ of 1.0 for signals near clipping.
+notes: When calibrated, feeding this function binary white noise at sample
+ values +32767 or -32768 should return values that approach 3.
+ ATHformula is used to approximate an equal loudness curve.
+future: Data indicates that the shape of the equal loudness curve varies
+ with intensity. This function might be improved by using an equal
+ loudness curve shaped for typical playback levels (instead of the
+ ATH, that is shaped for the threshold). A flexible realization might
+ simply bend the existing ATH curve to achieve the desired shape.
+ However, the potential gain may not be enough to justify an effort.
+*/
+static FLOAT
+psycho_loudness_approx(FLOAT const *energy, FLOAT const *eql_w)
+{
+ int i;
+ FLOAT loudness_power;
+
+ loudness_power = 0.0;
+ /* apply weights to power in freq. bands */
+ for (i = 0; i < BLKSIZE / 2; ++i)
+ loudness_power += energy[i] * eql_w[i];
+ loudness_power *= VO_SCALE;
+
+ return loudness_power;
+}
+
+/* mask_add optimization */
+/* init the limit values used to avoid computing log in mask_add when it is not necessary */
+
+/* For example, with i = 10*log10(m2/m1)/10*16 (= log10(m2/m1)*16)
+ *
+ * abs(i)>8 is equivalent (as i is an integer) to
+ * abs(i)>=9
+ * i>=9 || i<=-9
+ * equivalent to (as i is the biggest integer smaller than log10(m2/m1)*16
+ * or the smallest integer bigger than log10(m2/m1)*16 depending on the sign of log10(m2/m1)*16)
+ * log10(m2/m1)>=9/16 || log10(m2/m1)<=-9/16
+ * exp10 is strictly increasing thus this is equivalent to
+ * m2/m1 >= 10^(9/16) || m2/m1<=10^(-9/16) which are comparisons to constants
+ */
+
+
+#define I1LIMIT 8 /* as in if(i>8) */
+#define I2LIMIT 23 /* as in if(i>24) -> changed 23 */
+#define MLIMIT 15 /* as in if(m<15) */
+
+static FLOAT ma_max_i1;
+static FLOAT ma_max_i2;
+static FLOAT ma_max_m;
+
+ /*This is the masking table:
+ According to tonality, values are going from 0dB (TMN)
+ to 9.3dB (NMT).
+ After additive masking computation, 8dB are added, so
+ final values are going from 8dB to 17.3dB
+ */
+static const FLOAT tab[] = {
+ 1.0 /*pow(10, -0) */ ,
+ 0.79433 /*pow(10, -0.1) */ ,
+ 0.63096 /*pow(10, -0.2) */ ,
+ 0.63096 /*pow(10, -0.2) */ ,
+ 0.63096 /*pow(10, -0.2) */ ,
+ 0.63096 /*pow(10, -0.2) */ ,
+ 0.63096 /*pow(10, -0.2) */ ,
+ 0.25119 /*pow(10, -0.6) */ ,
+ 0.11749 /*pow(10, -0.93) */
+};
+
+static const int tab_mask_add_delta[] = { 2, 2, 2, 1, 1, 1, 0, 0, -1 };
+#define STATIC_ASSERT_EQUAL_DIMENSION(A,B) {extern char static_assert_##A[dimension_of(A) == dimension_of(B) ? 1 : -1];(void) static_assert_##A;}
+
+inline static int
+mask_add_delta(int i)
+{
+ STATIC_ASSERT_EQUAL_DIMENSION(tab_mask_add_delta,tab);
+ assert(i < (int)dimension_of(tab));
+ return tab_mask_add_delta[i];
+}
+
+
+static void
+init_mask_add_max_values(void)
+{
+ ma_max_i1 = pow(10, (I1LIMIT + 1) / 16.0);
+ ma_max_i2 = pow(10, (I2LIMIT + 1) / 16.0);
+ ma_max_m = pow(10, (MLIMIT) / 10.0);
+}
+
+
+
+
+/* addition of simultaneous masking Naoki Shibata 2000/7 */
+inline static FLOAT
+vbrpsy_mask_add(FLOAT m1, FLOAT m2, int b, int delta)
+{
+ static const FLOAT table2[] = {
+ 1.33352 * 1.33352, 1.35879 * 1.35879, 1.38454 * 1.38454, 1.39497 * 1.39497,
+ 1.40548 * 1.40548, 1.3537 * 1.3537, 1.30382 * 1.30382, 1.22321 * 1.22321,
+ 1.14758 * 1.14758,
+ 1
+ };
+
+ FLOAT ratio;
+
+ if (m1 < 0) {
+ m1 = 0;
+ }
+ if (m2 < 0) {
+ m2 = 0;
+ }
+ if (m1 <= 0) {
+ return m2;
+ }
+ if (m2 <= 0) {
+ return m1;
+ }
+ if (m2 > m1) {
+ ratio = m2 / m1;
+ }
+ else {
+ ratio = m1 / m2;
+ }
+ if (abs(b) <= delta) { /* approximately, 1 bark = 3 partitions */
+ /* originally 'if(i > 8)' */
+ if (ratio >= ma_max_i1) {
+ return m1 + m2;
+ }
+ else {
+ int i = (int) (FAST_LOG10_X(ratio, 16.0f));
+ return (m1 + m2) * table2[i];
+ }
+ }
+ if (ratio < ma_max_i2) {
+ return m1 + m2;
+ }
+ if (m1 < m2) {
+ m1 = m2;
+ }
+ return m1;
+}
+
+
+/* short block threshold calculation (part 2)
+
+ partition band bo_s[sfb] is at the transition from scalefactor
+ band sfb to the next one sfb+1; enn and thmm have to be split
+ between them
+*/
+static void
+convert_partition2scalefac(PsyConst_CB2SB_t const *const gd, FLOAT const *eb, FLOAT const *thr,
+ FLOAT enn_out[], FLOAT thm_out[])
+{
+ FLOAT enn, thmm;
+ int sb, b, n = gd->n_sb;
+ enn = thmm = 0.0f;
+ for (sb = b = 0; sb < n; ++b, ++sb) {
+ int const bo_sb = gd->bo[sb];
+ int const npart = gd->npart;
+ int const b_lim = bo_sb < npart ? bo_sb : npart;
+ while (b < b_lim) {
+ assert(eb[b] >= 0); /* iff failed, it may indicate some index error elsewhere */
+ assert(thr[b] >= 0);
+ enn += eb[b];
+ thmm += thr[b];
+ b++;
+ }
+ if (b >= npart) {
+ enn_out[sb] = enn;
+ thm_out[sb] = thmm;
+ ++sb;
+ break;
+ }
+ assert(eb[b] >= 0); /* iff failed, it may indicate some index error elsewhere */
+ assert(thr[b] >= 0);
+ {
+ /* at transition sfb -> sfb+1 */
+ FLOAT const w_curr = gd->bo_weight[sb];
+ FLOAT const w_next = 1.0f - w_curr;
+ enn += w_curr * eb[b];
+ thmm += w_curr * thr[b];
+ enn_out[sb] = enn;
+ thm_out[sb] = thmm;
+ enn = w_next * eb[b];
+ thmm = w_next * thr[b];
+ }
+ }
+ /* zero initialize the rest */
+ for (; sb < n; ++sb) {
+ enn_out[sb] = 0;
+ thm_out[sb] = 0;
+ }
+}
+
+static void
+convert_partition2scalefac_s(lame_internal_flags * gfc, FLOAT const *eb, FLOAT const *thr, int chn,
+ int sblock)
+{
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gds = &gfc->cd_psy->s;
+ FLOAT enn[SBMAX_s], thm[SBMAX_s];
+ int sb;
+ convert_partition2scalefac(gds, eb, thr, enn, thm);
+ for (sb = 0; sb < SBMAX_s; ++sb) {
+ psv->en[chn].s[sb][sblock] = enn[sb];
+ psv->thm[chn].s[sb][sblock] = thm[sb];
+ }
+}
+
+/* longblock threshold calculation (part 2) */
+static void
+convert_partition2scalefac_l(lame_internal_flags * gfc, FLOAT const *eb, FLOAT const *thr, int chn)
+{
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gdl = &gfc->cd_psy->l;
+ FLOAT *enn = &psv->en[chn].l[0];
+ FLOAT *thm = &psv->thm[chn].l[0];
+ convert_partition2scalefac(gdl, eb, thr, enn, thm);
+}
+
+static void
+convert_partition2scalefac_l_to_s(lame_internal_flags * gfc, FLOAT const *eb, FLOAT const *thr,
+ int chn)
+{
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gds = &gfc->cd_psy->l_to_s;
+ FLOAT enn[SBMAX_s], thm[SBMAX_s];
+ int sb, sblock;
+ convert_partition2scalefac(gds, eb, thr, enn, thm);
+ for (sb = 0; sb < SBMAX_s; ++sb) {
+ FLOAT const scale = 1. / 64.f;
+ FLOAT const tmp_enn = enn[sb];
+ FLOAT const tmp_thm = thm[sb] * scale;
+ for (sblock = 0; sblock < 3; ++sblock) {
+ psv->en[chn].s[sb][sblock] = tmp_enn;
+ psv->thm[chn].s[sb][sblock] = tmp_thm;
+ }
+ }
+}
+
+
+
+static inline FLOAT
+NS_INTERP(FLOAT x, FLOAT y, FLOAT r)
+{
+ /* was pow((x),(r))*pow((y),1-(r)) */
+ if (r >= 1.0f)
+ return x; /* 99.7% of the time */
+ if (r <= 0.0f)
+ return y;
+ if (y > 0.0f)
+ return powf(x / y, r) * y; /* rest of the time */
+ return 0.0f; /* never happens */
+}
+
+
+
+static FLOAT
+pecalc_s(III_psy_ratio const *mr, FLOAT masking_lower)
+{
+ FLOAT pe_s;
+ static const FLOAT regcoef_s[] = {
+ 11.8, /* these values are tuned only for 44.1kHz... */
+ 13.6,
+ 17.2,
+ 32,
+ 46.5,
+ 51.3,
+ 57.5,
+ 67.1,
+ 71.5,
+ 84.6,
+ 97.6,
+ 130,
+/* 255.8 */
+ };
+ unsigned int sb, sblock;
+
+ pe_s = 1236.28f / 4;
+ for (sb = 0; sb < SBMAX_s - 1; sb++) {
+ for (sblock = 0; sblock < 3; sblock++) {
+ FLOAT const thm = mr->thm.s[sb][sblock];
+ assert(sb < dimension_of(regcoef_s));
+ if (thm > 0.0f) {
+ FLOAT const x = thm * masking_lower;
+ FLOAT const en = mr->en.s[sb][sblock];
+ if (en > x) {
+ if (en > x * 1e10f) {
+ pe_s += regcoef_s[sb] * (10.0f * LOG10);
+ }
+ else {
+ assert(x > 0);
+ pe_s += regcoef_s[sb] * FAST_LOG10(en / x);
+ }
+ }
+ }
+ }
+ }
+
+ return pe_s;
+}
+
+static FLOAT
+pecalc_l(III_psy_ratio const *mr, FLOAT masking_lower)
+{
+ FLOAT pe_l;
+ static const FLOAT regcoef_l[] = {
+ 6.8, /* these values are tuned only for 44.1kHz... */
+ 5.8,
+ 5.8,
+ 6.4,
+ 6.5,
+ 9.9,
+ 12.1,
+ 14.4,
+ 15,
+ 18.9,
+ 21.6,
+ 26.9,
+ 34.2,
+ 40.2,
+ 46.8,
+ 56.5,
+ 60.7,
+ 73.9,
+ 85.7,
+ 93.4,
+ 126.1,
+/* 241.3 */
+ };
+ unsigned int sb;
+
+ pe_l = 1124.23f / 4;
+ for (sb = 0; sb < SBMAX_l - 1; sb++) {
+ FLOAT const thm = mr->thm.l[sb];
+ assert(sb < dimension_of(regcoef_l));
+ if (thm > 0.0f) {
+ FLOAT const x = thm * masking_lower;
+ FLOAT const en = mr->en.l[sb];
+ if (en > x) {
+ if (en > x * 1e10f) {
+ pe_l += regcoef_l[sb] * (10.0f * LOG10);
+ }
+ else {
+ assert(x > 0);
+ pe_l += regcoef_l[sb] * FAST_LOG10(en / x);
+ }
+ }
+ }
+ }
+
+ return pe_l;
+}
+
+
+static void
+calc_energy(PsyConst_CB2SB_t const *l, FLOAT const *fftenergy, FLOAT * eb, FLOAT * max, FLOAT * avg)
+{
+ int b, j;
+
+ for (b = j = 0; b < l->npart; ++b) {
+ FLOAT ebb = 0, m = 0;
+ int i;
+ for (i = 0; i < l->numlines[b]; ++i, ++j) {
+ FLOAT const el = fftenergy[j];
+ assert(el >= 0);
+ ebb += el;
+ if (m < el)
+ m = el;
+ }
+ eb[b] = ebb;
+ max[b] = m;
+ avg[b] = ebb * l->rnumlines[b];
+ assert(l->rnumlines[b] >= 0);
+ assert(ebb >= 0);
+ assert(eb[b] >= 0);
+ assert(max[b] >= 0);
+ assert(avg[b] >= 0);
+ }
+}
+
+
+static void
+calc_mask_index_l(lame_internal_flags const *gfc, FLOAT const *max,
+ FLOAT const *avg, unsigned char *mask_idx)
+{
+ PsyConst_CB2SB_t const *const gdl = &gfc->cd_psy->l;
+ FLOAT m, a;
+ int b, k;
+ int const last_tab_entry = sizeof(tab) / sizeof(tab[0]) - 1;
+ b = 0;
+ a = avg[b] + avg[b + 1];
+ assert(a >= 0);
+ if (a > 0.0f) {
+ m = max[b];
+ if (m < max[b + 1])
+ m = max[b + 1];
+ assert((gdl->numlines[b] + gdl->numlines[b + 1] - 1) > 0);
+ a = 20.0f * (m * 2.0f - a)
+ / (a * (gdl->numlines[b] + gdl->numlines[b + 1] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+
+ for (b = 1; b < gdl->npart - 1; b++) {
+ a = avg[b - 1] + avg[b] + avg[b + 1];
+ assert(a >= 0);
+ if (a > 0.0f) {
+ m = max[b - 1];
+ if (m < max[b])
+ m = max[b];
+ if (m < max[b + 1])
+ m = max[b + 1];
+ assert((gdl->numlines[b - 1] + gdl->numlines[b] + gdl->numlines[b + 1] - 1) > 0);
+ a = 20.0f * (m * 3.0f - a)
+ / (a * (gdl->numlines[b - 1] + gdl->numlines[b] + gdl->numlines[b + 1] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+ }
+ assert(b > 0);
+ assert(b == gdl->npart - 1);
+
+ a = avg[b - 1] + avg[b];
+ assert(a >= 0);
+ if (a > 0.0f) {
+ m = max[b - 1];
+ if (m < max[b])
+ m = max[b];
+ assert((gdl->numlines[b - 1] + gdl->numlines[b] - 1) > 0);
+ a = 20.0f * (m * 2.0f - a)
+ / (a * (gdl->numlines[b - 1] + gdl->numlines[b] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+ assert(b == (gdl->npart - 1));
+}
+
+
+static void
+vbrpsy_compute_fft_l(lame_internal_flags * gfc, const sample_t * const buffer[2], int chn,
+ int gr_out, FLOAT fftenergy[HBLKSIZE], FLOAT(*wsamp_l)[BLKSIZE])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ PsyStateVar_t *psv = &gfc->sv_psy;
+ plotting_data *plt = cfg->analysis ? gfc->pinfo : 0;
+ int j;
+
+ if (chn < 2) {
+ fft_long(gfc, *wsamp_l, chn, buffer);
+ }
+ else if (chn == 2) {
+ FLOAT const sqrt2_half = SQRT2 * 0.5f;
+ /* FFT data for mid and side channel is derived from L & R */
+ for (j = BLKSIZE - 1; j >= 0; --j) {
+ FLOAT const l = wsamp_l[0][j];
+ FLOAT const r = wsamp_l[1][j];
+ wsamp_l[0][j] = (l + r) * sqrt2_half;
+ wsamp_l[1][j] = (l - r) * sqrt2_half;
+ }
+ }
+
+ /*********************************************************************
+ * compute energies
+ *********************************************************************/
+ fftenergy[0] = wsamp_l[0][0];
+ fftenergy[0] *= fftenergy[0];
+
+ for (j = BLKSIZE / 2 - 1; j >= 0; --j) {
+ FLOAT const re = (*wsamp_l)[BLKSIZE / 2 - j];
+ FLOAT const im = (*wsamp_l)[BLKSIZE / 2 + j];
+ fftenergy[BLKSIZE / 2 - j] = (re * re + im * im) * 0.5f;
+ }
+ /* total energy */
+ {
+ FLOAT totalenergy = 0.0f;
+ for (j = 11; j < HBLKSIZE; j++)
+ totalenergy += fftenergy[j];
+
+ psv->tot_ener[chn] = totalenergy;
+ }
+
+ if (plt) {
+ for (j = 0; j < HBLKSIZE; j++) {
+ plt->energy[gr_out][chn][j] = plt->energy_save[chn][j];
+ plt->energy_save[chn][j] = fftenergy[j];
+ }
+ }
+}
+
+
+static void
+vbrpsy_compute_fft_s(lame_internal_flags const *gfc, const sample_t * const buffer[2], int chn,
+ int sblock, FLOAT(*fftenergy_s)[HBLKSIZE_s], FLOAT(*wsamp_s)[3][BLKSIZE_s])
+{
+ int j;
+
+ if (sblock == 0 && chn < 2) {
+ fft_short(gfc, *wsamp_s, chn, buffer);
+ }
+ if (chn == 2) {
+ FLOAT const sqrt2_half = SQRT2 * 0.5f;
+ /* FFT data for mid and side channel is derived from L & R */
+ for (j = BLKSIZE_s - 1; j >= 0; --j) {
+ FLOAT const l = wsamp_s[0][sblock][j];
+ FLOAT const r = wsamp_s[1][sblock][j];
+ wsamp_s[0][sblock][j] = (l + r) * sqrt2_half;
+ wsamp_s[1][sblock][j] = (l - r) * sqrt2_half;
+ }
+ }
+
+ /*********************************************************************
+ * compute energies
+ *********************************************************************/
+ fftenergy_s[sblock][0] = (*wsamp_s)[sblock][0];
+ fftenergy_s[sblock][0] *= fftenergy_s[sblock][0];
+ for (j = BLKSIZE_s / 2 - 1; j >= 0; --j) {
+ FLOAT const re = (*wsamp_s)[sblock][BLKSIZE_s / 2 - j];
+ FLOAT const im = (*wsamp_s)[sblock][BLKSIZE_s / 2 + j];
+ fftenergy_s[sblock][BLKSIZE_s / 2 - j] = (re * re + im * im) * 0.5f;
+ }
+}
+
+
+ /*********************************************************************
+ * compute loudness approximation (used for ATH auto-level adjustment)
+ *********************************************************************/
+static void
+vbrpsy_compute_loudness_approximation_l(lame_internal_flags * gfc, int gr_out, int chn,
+ const FLOAT fftenergy[HBLKSIZE])
+{
+ PsyStateVar_t *psv = &gfc->sv_psy;
+ if (chn < 2) { /*no loudness for mid/side ch */
+ gfc->ov_psy.loudness_sq[gr_out][chn] = psv->loudness_sq_save[chn];
+ psv->loudness_sq_save[chn] = psycho_loudness_approx(fftenergy, gfc->ATH->eql_w);
+ }
+}
+
+
+ /**********************************************************************
+ * Apply HPF of fs/4 to the input signal.
+ * This is used for attack detection / handling.
+ **********************************************************************/
+static void
+vbrpsy_attack_detection(lame_internal_flags * gfc, const sample_t * const buffer[2], int gr_out,
+ III_psy_ratio masking_ratio[2][2], III_psy_ratio masking_MS_ratio[2][2],
+ FLOAT energy[4], FLOAT sub_short_factor[4][3], int ns_attacks[4][4],
+ int uselongblock[2])
+{
+ FLOAT ns_hpfsmpl[2][576];
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ plotting_data *plt = cfg->analysis ? gfc->pinfo : 0;
+ int const n_chn_out = cfg->channels_out;
+ /* chn=2 and 3 = Mid and Side channels */
+ int const n_chn_psy = (cfg->mode == JOINT_STEREO) ? 4 : n_chn_out;
+ int chn, i, j;
+
+ memset(&ns_hpfsmpl[0][0], 0, sizeof(ns_hpfsmpl));
+ /* Don't copy the input buffer into a temporary buffer */
+ /* unroll the loop 2 times */
+ for (chn = 0; chn < n_chn_out; chn++) {
+ static const FLOAT fircoef[] = {
+ -8.65163e-18 * 2, -0.00851586 * 2, -6.74764e-18 * 2, 0.0209036 * 2,
+ -3.36639e-17 * 2, -0.0438162 * 2, -1.54175e-17 * 2, 0.0931738 * 2,
+ -5.52212e-17 * 2, -0.313819 * 2
+ };
+ /* apply high pass filter of fs/4 */
+ const sample_t *const firbuf = &buffer[chn][576 - 350 - NSFIRLEN + 192];
+ assert(dimension_of(fircoef) == ((NSFIRLEN - 1) / 2));
+ for (i = 0; i < 576; i++) {
+ FLOAT sum1, sum2;
+ sum1 = firbuf[i + 10];
+ sum2 = 0.0;
+ for (j = 0; j < ((NSFIRLEN - 1) / 2) - 1; j += 2) {
+ sum1 += fircoef[j] * (firbuf[i + j] + firbuf[i + NSFIRLEN - j]);
+ sum2 += fircoef[j + 1] * (firbuf[i + j + 1] + firbuf[i + NSFIRLEN - j - 1]);
+ }
+ ns_hpfsmpl[chn][i] = sum1 + sum2;
+ }
+ masking_ratio[gr_out][chn].en = psv->en[chn];
+ masking_ratio[gr_out][chn].thm = psv->thm[chn];
+ if (n_chn_psy > 2) {
+ /* MS maskings */
+ /*percep_MS_entropy [chn-2] = gfc -> pe [chn]; */
+ masking_MS_ratio[gr_out][chn].en = psv->en[chn + 2];
+ masking_MS_ratio[gr_out][chn].thm = psv->thm[chn + 2];
+ }
+ }
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ FLOAT attack_intensity[12];
+ FLOAT en_subshort[12];
+ FLOAT en_short[4] = { 0, 0, 0, 0 };
+ FLOAT const *pf = ns_hpfsmpl[chn & 1];
+ int ns_uselongblock = 1;
+
+ if (chn == 2) {
+ for (i = 0, j = 576; j > 0; ++i, --j) {
+ FLOAT const l = ns_hpfsmpl[0][i];
+ FLOAT const r = ns_hpfsmpl[1][i];
+ ns_hpfsmpl[0][i] = l + r;
+ ns_hpfsmpl[1][i] = l - r;
+ }
+ }
+ /***************************************************************
+ * determine the block type (window type)
+ ***************************************************************/
+ /* calculate energies of each sub-shortblocks */
+ for (i = 0; i < 3; i++) {
+ en_subshort[i] = psv->last_en_subshort[chn][i + 6];
+ assert(psv->last_en_subshort[chn][i + 4] > 0);
+ attack_intensity[i] = en_subshort[i] / psv->last_en_subshort[chn][i + 4];
+ en_short[0] += en_subshort[i];
+ }
+
+ for (i = 0; i < 9; i++) {
+ FLOAT const *const pfe = pf + 576 / 9;
+ FLOAT p = 1.;
+ for (; pf < pfe; pf++)
+ if (p < fabs(*pf))
+ p = fabs(*pf);
+ psv->last_en_subshort[chn][i] = en_subshort[i + 3] = p;
+ en_short[1 + i / 3] += p;
+ if (p > en_subshort[i + 3 - 2]) {
+ assert(en_subshort[i + 3 - 2] > 0);
+ p = p / en_subshort[i + 3 - 2];
+ }
+ else if (en_subshort[i + 3 - 2] > p * 10.0f) {
+ assert(p > 0);
+ p = en_subshort[i + 3 - 2] / (p * 10.0f);
+ }
+ else {
+ p = 0.0;
+ }
+ attack_intensity[i + 3] = p;
+ }
+
+ /* pulse like signal detection for fatboy.wav and so on */
+ for (i = 0; i < 3; ++i) {
+ FLOAT const enn =
+ en_subshort[i * 3 + 3] + en_subshort[i * 3 + 4] + en_subshort[i * 3 + 5];
+ FLOAT factor = 1.f;
+ if (en_subshort[i * 3 + 5] * 6 < enn) {
+ factor *= 0.5f;
+ if (en_subshort[i * 3 + 4] * 6 < enn) {
+ factor *= 0.5f;
+ }
+ }
+ sub_short_factor[chn][i] = factor;
+ }
+
+ if (plt) {
+ FLOAT x = attack_intensity[0];
+ for (i = 1; i < 12; i++) {
+ if (x < attack_intensity[i]) {
+ x = attack_intensity[i];
+ }
+ }
+ plt->ers[gr_out][chn] = plt->ers_save[chn];
+ plt->ers_save[chn] = x;
+ }
+
+ /* compare energies between sub-shortblocks */
+ {
+ FLOAT x = gfc->cd_psy->attack_threshold[chn];
+ for (i = 0; i < 12; i++) {
+ if (ns_attacks[chn][i / 3] == 0) {
+ if (attack_intensity[i] > x) {
+ ns_attacks[chn][i / 3] = (i % 3) + 1;
+ }
+ }
+ }
+ }
+ /* should have energy change between short blocks, in order to avoid periodic signals */
+ /* Good samples to show the effect are Trumpet test songs */
+ /* GB: tuned (1) to avoid too many short blocks for test sample TRUMPET */
+ /* RH: tuned (2) to let enough short blocks through for test sample FSOL and SNAPS */
+ for (i = 1; i < 4; i++) {
+ FLOAT const u = en_short[i - 1];
+ FLOAT const v = en_short[i];
+ FLOAT const m = Max(u, v);
+ if (m < 40000) { /* (2) */
+ if (u < 1.7f * v && v < 1.7f * u) { /* (1) */
+ if (i == 1 && ns_attacks[chn][0] <= ns_attacks[chn][i]) {
+ ns_attacks[chn][0] = 0;
+ }
+ ns_attacks[chn][i] = 0;
+ }
+ }
+ }
+
+ if (ns_attacks[chn][0] <= psv->last_attacks[chn]) {
+ ns_attacks[chn][0] = 0;
+ }
+
+ if (psv->last_attacks[chn] == 3 ||
+ ns_attacks[chn][0] + ns_attacks[chn][1] + ns_attacks[chn][2] + ns_attacks[chn][3]) {
+ ns_uselongblock = 0;
+
+ if (ns_attacks[chn][1] && ns_attacks[chn][0]) {
+ ns_attacks[chn][1] = 0;
+ }
+ if (ns_attacks[chn][2] && ns_attacks[chn][1]) {
+ ns_attacks[chn][2] = 0;
+ }
+ if (ns_attacks[chn][3] && ns_attacks[chn][2]) {
+ ns_attacks[chn][3] = 0;
+ }
+ }
+
+ if (chn < 2) {
+ uselongblock[chn] = ns_uselongblock;
+ }
+ else {
+ if (ns_uselongblock == 0) {
+ uselongblock[0] = uselongblock[1] = 0;
+ }
+ }
+
+ /* there is a one granule delay. Copy maskings computed last call
+ * into masking_ratio to return to calling program.
+ */
+ energy[chn] = psv->tot_ener[chn];
+ }
+}
+
+
+static void
+vbrpsy_skip_masking_s(lame_internal_flags * gfc, int chn, int sblock)
+{
+ if (sblock == 0) {
+ FLOAT *nbs2 = &gfc->sv_psy.nb_s2[chn][0];
+ FLOAT *nbs1 = &gfc->sv_psy.nb_s1[chn][0];
+ int const n = gfc->cd_psy->s.npart;
+ int b;
+ for (b = 0; b < n; b++) {
+ nbs2[b] = nbs1[b];
+ }
+ }
+}
+
+
+static void
+vbrpsy_calc_mask_index_s(lame_internal_flags const *gfc, FLOAT const *max,
+ FLOAT const *avg, unsigned char *mask_idx)
+{
+ PsyConst_CB2SB_t const *const gds = &gfc->cd_psy->s;
+ FLOAT m, a;
+ int b, k;
+ int const last_tab_entry = dimension_of(tab) - 1;
+ b = 0;
+ a = avg[b] + avg[b + 1];
+ assert(a >= 0);
+ if (a > 0.0f) {
+ m = max[b];
+ if (m < max[b + 1])
+ m = max[b + 1];
+ assert((gds->numlines[b] + gds->numlines[b + 1] - 1) > 0);
+ a = 20.0f * (m * 2.0f - a)
+ / (a * (gds->numlines[b] + gds->numlines[b + 1] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+
+ for (b = 1; b < gds->npart - 1; b++) {
+ a = avg[b - 1] + avg[b] + avg[b + 1];
+ assert(b + 1 < gds->npart);
+ assert(a >= 0);
+ if (a > 0.0) {
+ m = max[b - 1];
+ if (m < max[b])
+ m = max[b];
+ if (m < max[b + 1])
+ m = max[b + 1];
+ assert((gds->numlines[b - 1] + gds->numlines[b] + gds->numlines[b + 1] - 1) > 0);
+ a = 20.0f * (m * 3.0f - a)
+ / (a * (gds->numlines[b - 1] + gds->numlines[b] + gds->numlines[b + 1] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+ }
+ assert(b > 0);
+ assert(b == gds->npart - 1);
+
+ a = avg[b - 1] + avg[b];
+ assert(a >= 0);
+ if (a > 0.0f) {
+ m = max[b - 1];
+ if (m < max[b])
+ m = max[b];
+ assert((gds->numlines[b - 1] + gds->numlines[b] - 1) > 0);
+ a = 20.0f * (m * 2.0f - a)
+ / (a * (gds->numlines[b - 1] + gds->numlines[b] - 1));
+ k = (int) a;
+ if (k > last_tab_entry)
+ k = last_tab_entry;
+ mask_idx[b] = k;
+ }
+ else {
+ mask_idx[b] = 0;
+ }
+ assert(b == (gds->npart - 1));
+}
+
+
+static void
+vbrpsy_compute_masking_s(lame_internal_flags * gfc, const FLOAT(*fftenergy_s)[HBLKSIZE_s],
+ FLOAT * eb, FLOAT * thr, int chn, int sblock)
+{
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gds = &gfc->cd_psy->s;
+ FLOAT max[CBANDS], avg[CBANDS];
+ int i, j, b;
+ unsigned char mask_idx_s[CBANDS];
+
+ memset(max, 0, sizeof(max));
+ memset(avg, 0, sizeof(avg));
+
+ for (b = j = 0; b < gds->npart; ++b) {
+ FLOAT ebb = 0, m = 0;
+ int const n = gds->numlines[b];
+ for (i = 0; i < n; ++i, ++j) {
+ FLOAT const el = fftenergy_s[sblock][j];
+ ebb += el;
+ if (m < el)
+ m = el;
+ }
+ eb[b] = ebb;
+ assert(ebb >= 0);
+ max[b] = m;
+ assert(n > 0);
+ avg[b] = ebb * gds->rnumlines[b];
+ assert(avg[b] >= 0);
+ }
+ assert(b == gds->npart);
+ assert(j == 129);
+ vbrpsy_calc_mask_index_s(gfc, max, avg, mask_idx_s);
+ for (j = b = 0; b < gds->npart; b++) {
+ int kk = gds->s3ind[b][0];
+ int const last = gds->s3ind[b][1];
+ int const delta = mask_add_delta(mask_idx_s[b]);
+ int dd, dd_n;
+ FLOAT x, ecb, avg_mask;
+ FLOAT const masking_lower = gds->masking_lower[b] * gfc->sv_qnt.masking_lower;
+
+ dd = mask_idx_s[kk];
+ dd_n = 1;
+ ecb = gds->s3[j] * eb[kk] * tab[mask_idx_s[kk]];
+ ++j, ++kk;
+ while (kk <= last) {
+ dd += mask_idx_s[kk];
+ dd_n += 1;
+ x = gds->s3[j] * eb[kk] * tab[mask_idx_s[kk]];
+ ecb = vbrpsy_mask_add(ecb, x, kk - b, delta);
+ ++j, ++kk;
+ }
+ dd = (1 + 2 * dd) / (2 * dd_n);
+ avg_mask = tab[dd] * 0.5f;
+ ecb *= avg_mask;
+#if 0 /* we can do PRE ECHO control now here, or do it later */
+ if (psv->blocktype_old[chn & 0x01] == SHORT_TYPE) {
+ /* limit calculated threshold by even older granule */
+ FLOAT const t1 = rpelev_s * psv->nb_s1[chn][b];
+ FLOAT const t2 = rpelev2_s * psv->nb_s2[chn][b];
+ FLOAT const tm = (t2 > 0) ? Min(ecb, t2) : ecb;
+ thr[b] = (t1 > 0) ? NS_INTERP(Min(tm, t1), ecb, 0.6) : ecb;
+ }
+ else {
+ /* limit calculated threshold by older granule */
+ FLOAT const t1 = rpelev_s * psv->nb_s1[chn][b];
+ thr[b] = (t1 > 0) ? NS_INTERP(Min(ecb, t1), ecb, 0.6) : ecb;
+ }
+#else /* we do it later */
+ thr[b] = ecb;
+#endif
+ psv->nb_s2[chn][b] = psv->nb_s1[chn][b];
+ psv->nb_s1[chn][b] = ecb;
+ {
+ /* if THR exceeds EB, the quantization routines will take the difference
+ * from other bands. in case of strong tonal samples (tonaltest.wav)
+ * this leads to heavy distortions. that's why we limit THR here.
+ */
+ x = max[b];
+ x *= gds->minval[b];
+ x *= avg_mask;
+ if (thr[b] > x) {
+ thr[b] = x;
+ }
+ }
+ if (masking_lower > 1) {
+ thr[b] *= masking_lower;
+ }
+ if (thr[b] > eb[b]) {
+ thr[b] = eb[b];
+ }
+ if (masking_lower < 1) {
+ thr[b] *= masking_lower;
+ }
+
+ assert(thr[b] >= 0);
+ }
+ for (; b < CBANDS; ++b) {
+ eb[b] = 0;
+ thr[b] = 0;
+ }
+}
+
+
+static void
+vbrpsy_compute_masking_l(lame_internal_flags * gfc, const FLOAT fftenergy[HBLKSIZE],
+ FLOAT eb_l[CBANDS], FLOAT thr[CBANDS], int chn)
+{
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gdl = &gfc->cd_psy->l;
+ FLOAT max[CBANDS], avg[CBANDS];
+ unsigned char mask_idx_l[CBANDS + 2];
+ int k, b;
+
+ /*********************************************************************
+ * Calculate the energy and the tonality of each partition.
+ *********************************************************************/
+ calc_energy(gdl, fftenergy, eb_l, max, avg);
+ calc_mask_index_l(gfc, max, avg, mask_idx_l);
+
+ /*********************************************************************
+ * convolve the partitioned energy and unpredictability
+ * with the spreading function, s3_l[b][k]
+ ********************************************************************/
+ k = 0;
+ for (b = 0; b < gdl->npart; b++) {
+ FLOAT x, ecb, avg_mask, t;
+ FLOAT const masking_lower = gdl->masking_lower[b] * gfc->sv_qnt.masking_lower;
+ /* convolve the partitioned energy with the spreading function */
+ int kk = gdl->s3ind[b][0];
+ int const last = gdl->s3ind[b][1];
+ int const delta = mask_add_delta(mask_idx_l[b]);
+ int dd = 0, dd_n = 0;
+
+ dd = mask_idx_l[kk];
+ dd_n += 1;
+ ecb = gdl->s3[k] * eb_l[kk] * tab[mask_idx_l[kk]];
+ ++k, ++kk;
+ while (kk <= last) {
+ dd += mask_idx_l[kk];
+ dd_n += 1;
+ x = gdl->s3[k] * eb_l[kk] * tab[mask_idx_l[kk]];
+ t = vbrpsy_mask_add(ecb, x, kk - b, delta);
+#if 0
+ ecb += eb_l[kk];
+ if (ecb > t) {
+ ecb = t;
+ }
+#else
+ ecb = t;
+#endif
+ ++k, ++kk;
+ }
+ dd = (1 + 2 * dd) / (2 * dd_n);
+ avg_mask = tab[dd] * 0.5f;
+ ecb *= avg_mask;
+
+ /**** long block pre-echo control ****/
+ /* dont use long block pre-echo control if previous granule was
+ * a short block. This is to avoid the situation:
+ * frame0: quiet (very low masking)
+ * frame1: surge (triggers short blocks)
+ * frame2: regular frame. looks like pre-echo when compared to
+ * frame0, but all pre-echo was in frame1.
+ */
+ /* chn=0,1 L and R channels
+ chn=2,3 S and M channels.
+ */
+ if (psv->blocktype_old[chn & 0x01] == SHORT_TYPE) {
+ FLOAT const ecb_limit = rpelev * psv->nb_l1[chn][b];
+ if (ecb_limit > 0) {
+ thr[b] = Min(ecb, ecb_limit);
+ }
+ else {
+ /* Robert 071209:
+ Because we don't calculate long block psy when we know a granule
+ should be of short blocks, we don't have any clue how the granule
+ before would have looked like as a long block. So we have to guess
+ a little bit for this END_TYPE block.
+ Most of the time we get away with this sloppyness. (fingers crossed :)
+ The speed increase is worth it.
+ */
+ thr[b] = Min(ecb, eb_l[b] * NS_PREECHO_ATT2);
+ }
+ }
+ else {
+ FLOAT ecb_limit_2 = rpelev2 * psv->nb_l2[chn][b];
+ FLOAT ecb_limit_1 = rpelev * psv->nb_l1[chn][b];
+ FLOAT ecb_limit;
+ if (ecb_limit_2 <= 0) {
+ ecb_limit_2 = ecb;
+ }
+ if (ecb_limit_1 <= 0) {
+ ecb_limit_1 = ecb;
+ }
+ if (psv->blocktype_old[chn & 0x01] == NORM_TYPE) {
+ ecb_limit = Min(ecb_limit_1, ecb_limit_2);
+ }
+ else {
+ ecb_limit = ecb_limit_1;
+ }
+ thr[b] = Min(ecb, ecb_limit);
+ }
+ psv->nb_l2[chn][b] = psv->nb_l1[chn][b];
+ psv->nb_l1[chn][b] = ecb;
+ {
+ /* if THR exceeds EB, the quantization routines will take the difference
+ * from other bands. in case of strong tonal samples (tonaltest.wav)
+ * this leads to heavy distortions. that's why we limit THR here.
+ */
+ x = max[b];
+ x *= gdl->minval[b];
+ x *= avg_mask;
+ if (thr[b] > x) {
+ thr[b] = x;
+ }
+ }
+ if (masking_lower > 1) {
+ thr[b] *= masking_lower;
+ }
+ if (thr[b] > eb_l[b]) {
+ thr[b] = eb_l[b];
+ }
+ if (masking_lower < 1) {
+ thr[b] *= masking_lower;
+ }
+ assert(thr[b] >= 0);
+ }
+ for (; b < CBANDS; ++b) {
+ eb_l[b] = 0;
+ thr[b] = 0;
+ }
+}
+
+
+static void
+vbrpsy_compute_block_type(SessionConfig_t const *cfg, int *uselongblock)
+{
+ int chn;
+
+ if (cfg->short_blocks == short_block_coupled
+ /* force both channels to use the same block type */
+ /* this is necessary if the frame is to be encoded in ms_stereo. */
+ /* But even without ms_stereo, FhG does this */
+ && !(uselongblock[0] && uselongblock[1]))
+ uselongblock[0] = uselongblock[1] = 0;
+
+ for (chn = 0; chn < cfg->channels_out; chn++) {
+ /* disable short blocks */
+ if (cfg->short_blocks == short_block_dispensed) {
+ uselongblock[chn] = 1;
+ }
+ if (cfg->short_blocks == short_block_forced) {
+ uselongblock[chn] = 0;
+ }
+ }
+}
+
+
+static void
+vbrpsy_apply_block_type(PsyStateVar_t * psv, int nch, int const *uselongblock, int *blocktype_d)
+{
+ int chn;
+
+ /* update the blocktype of the previous granule, since it depends on what
+ * happend in this granule */
+ for (chn = 0; chn < nch; chn++) {
+ int blocktype = NORM_TYPE;
+ /* disable short blocks */
+
+ if (uselongblock[chn]) {
+ /* no attack : use long blocks */
+ assert(psv->blocktype_old[chn] != START_TYPE);
+ if (psv->blocktype_old[chn] == SHORT_TYPE)
+ blocktype = STOP_TYPE;
+ }
+ else {
+ /* attack : use short blocks */
+ blocktype = SHORT_TYPE;
+ if (psv->blocktype_old[chn] == NORM_TYPE) {
+ psv->blocktype_old[chn] = START_TYPE;
+ }
+ if (psv->blocktype_old[chn] == STOP_TYPE)
+ psv->blocktype_old[chn] = SHORT_TYPE;
+ }
+
+ blocktype_d[chn] = psv->blocktype_old[chn]; /* value returned to calling program */
+ psv->blocktype_old[chn] = blocktype; /* save for next call to l3psy_anal */
+ }
+}
+
+
+/***************************************************************
+ * compute M/S thresholds from Johnston & Ferreira 1992 ICASSP paper
+ ***************************************************************/
+
+static void
+vbrpsy_compute_MS_thresholds(const FLOAT eb[4][CBANDS], FLOAT thr[4][CBANDS],
+ const FLOAT cb_mld[CBANDS], const FLOAT ath_cb[CBANDS], FLOAT athlower,
+ FLOAT msfix, int n)
+{
+ FLOAT const msfix2 = msfix * 2.f;
+ FLOAT rside, rmid;
+ int b;
+ for (b = 0; b < n; ++b) {
+ FLOAT const ebM = eb[2][b];
+ FLOAT const ebS = eb[3][b];
+ FLOAT const thmL = thr[0][b];
+ FLOAT const thmR = thr[1][b];
+ FLOAT thmM = thr[2][b];
+ FLOAT thmS = thr[3][b];
+
+ /* use this fix if L & R masking differs by 2db or less */
+ /* if db = 10*log10(x2/x1) < 2 */
+ /* if (x2 < 1.58*x1) { */
+ if (thmL <= 1.58f * thmR && thmR <= 1.58f * thmL) {
+ FLOAT const mld_m = cb_mld[b] * ebS;
+ FLOAT const mld_s = cb_mld[b] * ebM;
+ FLOAT const tmp_m = Min(thmS, mld_m);
+ FLOAT const tmp_s = Min(thmM, mld_s);
+ rmid = Max(thmM, tmp_m);
+ rside = Max(thmS, tmp_s);
+ }
+ else {
+ rmid = thmM;
+ rside = thmS;
+ }
+ if (msfix > 0.f) {
+ /***************************************************************/
+ /* Adjust M/S maskings if user set "msfix" */
+ /***************************************************************/
+ /* Naoki Shibata 2000 */
+ FLOAT thmLR, thmMS;
+ FLOAT const ath = ath_cb[b] * athlower;
+ FLOAT const tmp_l = Max(thmL, ath);
+ FLOAT const tmp_r = Max(thmR, ath);
+ thmLR = Min(tmp_l, tmp_r);
+ thmM = Max(rmid, ath);
+ thmS = Max(rside, ath);
+ thmMS = thmM + thmS;
+ if (thmMS > 0.f && (thmLR * msfix2) < thmMS) {
+ FLOAT const f = thmLR * msfix2 / thmMS;
+ thmM *= f;
+ thmS *= f;
+ assert(thmMS > 0.f);
+ }
+ rmid = Min(thmM, rmid);
+ rside = Min(thmS, rside);
+ }
+ if (rmid > ebM) {
+ rmid = ebM;
+ }
+ if (rside > ebS) {
+ rside = ebS;
+ }
+ thr[2][b] = rmid;
+ thr[3][b] = rside;
+ }
+}
+
+
+/*
+ * NOTE: the bitrate reduction from the inter-channel masking effect is low
+ * compared to the chance of getting annyoing artefacts. L3psycho_anal_vbr does
+ * not use this feature. (Robert 071216)
+*/
+
+int
+L3psycho_anal_vbr(lame_internal_flags * gfc,
+ const sample_t * const buffer[2], int gr_out,
+ III_psy_ratio masking_ratio[2][2],
+ III_psy_ratio masking_MS_ratio[2][2],
+ FLOAT percep_entropy[2], FLOAT percep_MS_entropy[2],
+ FLOAT energy[4], int blocktype_d[2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_CB2SB_t const *const gdl = &gfc->cd_psy->l;
+ PsyConst_CB2SB_t const *const gds = &gfc->cd_psy->s;
+ plotting_data *plt = cfg->analysis ? gfc->pinfo : 0;
+
+ III_psy_xmin last_thm[4];
+
+ /* fft and energy calculation */
+ FLOAT(*wsamp_l)[BLKSIZE];
+ FLOAT(*wsamp_s)[3][BLKSIZE_s];
+ FLOAT fftenergy[HBLKSIZE];
+ FLOAT fftenergy_s[3][HBLKSIZE_s];
+ FLOAT wsamp_L[2][BLKSIZE];
+ FLOAT wsamp_S[2][3][BLKSIZE_s];
+ FLOAT eb[4][CBANDS], thr[4][CBANDS];
+
+ FLOAT sub_short_factor[4][3];
+ FLOAT thmm;
+ FLOAT const pcfact = 0.6f;
+ FLOAT const ath_factor =
+ (cfg->msfix > 0.f) ? (cfg->ATH_offset_factor * gfc->ATH->adjust_factor) : 1.f;
+
+ const FLOAT(*const_eb)[CBANDS] = (const FLOAT(*)[CBANDS]) eb;
+ const FLOAT(*const_fftenergy_s)[HBLKSIZE_s] = (const FLOAT(*)[HBLKSIZE_s]) fftenergy_s;
+
+ /* block type */
+ int ns_attacks[4][4] = { {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0} };
+ int uselongblock[2];
+
+ /* usual variables like loop indices, etc.. */
+ int chn, sb, sblock;
+
+ /* chn=2 and 3 = Mid and Side channels */
+ int const n_chn_psy = (cfg->mode == JOINT_STEREO) ? 4 : cfg->channels_out;
+
+ memcpy(&last_thm[0], &psv->thm[0], sizeof(last_thm));
+
+ vbrpsy_attack_detection(gfc, buffer, gr_out, masking_ratio, masking_MS_ratio, energy,
+ sub_short_factor, ns_attacks, uselongblock);
+
+ vbrpsy_compute_block_type(cfg, uselongblock);
+
+ /* LONG BLOCK CASE */
+ {
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ int const ch01 = chn & 0x01;
+
+ wsamp_l = wsamp_L + ch01;
+ vbrpsy_compute_fft_l(gfc, buffer, chn, gr_out, fftenergy, wsamp_l);
+ vbrpsy_compute_loudness_approximation_l(gfc, gr_out, chn, fftenergy);
+ vbrpsy_compute_masking_l(gfc, fftenergy, eb[chn], thr[chn], chn);
+ }
+ if (cfg->mode == JOINT_STEREO) {
+ if ((uselongblock[0] + uselongblock[1]) == 2) {
+ vbrpsy_compute_MS_thresholds(const_eb, thr, gdl->mld_cb, gfc->ATH->cb_l,
+ ath_factor, cfg->msfix, gdl->npart);
+ }
+ }
+ /* TODO: apply adaptive ATH masking here ?? */
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ convert_partition2scalefac_l(gfc, eb[chn], thr[chn], chn);
+ convert_partition2scalefac_l_to_s(gfc, eb[chn], thr[chn], chn);
+ }
+ }
+ /* SHORT BLOCKS CASE */
+ {
+ int const force_short_block_calc = gfc->cd_psy->force_short_block_calc;
+ for (sblock = 0; sblock < 3; sblock++) {
+ for (chn = 0; chn < n_chn_psy; ++chn) {
+ int const ch01 = chn & 0x01;
+ if (uselongblock[ch01] && !force_short_block_calc) {
+ vbrpsy_skip_masking_s(gfc, chn, sblock);
+ }
+ else {
+ /* compute masking thresholds for short blocks */
+ wsamp_s = wsamp_S + ch01;
+ vbrpsy_compute_fft_s(gfc, buffer, chn, sblock, fftenergy_s, wsamp_s);
+ vbrpsy_compute_masking_s(gfc, const_fftenergy_s, eb[chn], thr[chn], chn,
+ sblock);
+ }
+ }
+ if (cfg->mode == JOINT_STEREO) {
+ if ((uselongblock[0] + uselongblock[1]) == 0) {
+ vbrpsy_compute_MS_thresholds(const_eb, thr, gds->mld_cb, gfc->ATH->cb_s,
+ ath_factor, cfg->msfix, gds->npart);
+ }
+ }
+ /* TODO: apply adaptive ATH masking here ?? */
+ for (chn = 0; chn < n_chn_psy; ++chn) {
+ int const ch01 = chn & 0x01;
+ if (!uselongblock[ch01] || force_short_block_calc) {
+ convert_partition2scalefac_s(gfc, eb[chn], thr[chn], chn, sblock);
+ }
+ }
+ }
+
+ /**** short block pre-echo control ****/
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ for (sb = 0; sb < SBMAX_s; sb++) {
+ FLOAT new_thmm[3], prev_thm, t1, t2;
+ for (sblock = 0; sblock < 3; sblock++) {
+ thmm = psv->thm[chn].s[sb][sblock];
+ thmm *= NS_PREECHO_ATT0;
+
+ t1 = t2 = thmm;
+
+ if (sblock > 0) {
+ prev_thm = new_thmm[sblock - 1];
+ }
+ else {
+ prev_thm = last_thm[chn].s[sb][2];
+ }
+ if (ns_attacks[chn][sblock] >= 2 || ns_attacks[chn][sblock + 1] == 1) {
+ t1 = NS_INTERP(prev_thm, thmm, NS_PREECHO_ATT1 * pcfact);
+ }
+ thmm = Min(t1, thmm);
+ if (ns_attacks[chn][sblock] == 1) {
+ t2 = NS_INTERP(prev_thm, thmm, NS_PREECHO_ATT2 * pcfact);
+ }
+ else if ((sblock == 0 && psv->last_attacks[chn] == 3)
+ || (sblock > 0 && ns_attacks[chn][sblock - 1] == 3)) { /* 2nd preceeding block */
+ switch (sblock) {
+ case 0:
+ prev_thm = last_thm[chn].s[sb][1];
+ break;
+ case 1:
+ prev_thm = last_thm[chn].s[sb][2];
+ break;
+ case 2:
+ prev_thm = new_thmm[0];
+ break;
+ }
+ t2 = NS_INTERP(prev_thm, thmm, NS_PREECHO_ATT2 * pcfact);
+ }
+
+ thmm = Min(t1, thmm);
+ thmm = Min(t2, thmm);
+
+ /* pulse like signal detection for fatboy.wav and so on */
+ thmm *= sub_short_factor[chn][sblock];
+
+ new_thmm[sblock] = thmm;
+ }
+ for (sblock = 0; sblock < 3; sblock++) {
+ psv->thm[chn].s[sb][sblock] = new_thmm[sblock];
+ }
+ }
+ }
+ }
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ psv->last_attacks[chn] = ns_attacks[chn][2];
+ }
+
+
+ /***************************************************************
+ * determine final block type
+ ***************************************************************/
+ vbrpsy_apply_block_type(psv, cfg->channels_out, uselongblock, blocktype_d);
+
+ /*********************************************************************
+ * compute the value of PE to return ... no delay and advance
+ *********************************************************************/
+ for (chn = 0; chn < n_chn_psy; chn++) {
+ FLOAT *ppe;
+ int type;
+ III_psy_ratio const *mr;
+
+ if (chn > 1) {
+ ppe = percep_MS_entropy - 2;
+ type = NORM_TYPE;
+ if (blocktype_d[0] == SHORT_TYPE || blocktype_d[1] == SHORT_TYPE)
+ type = SHORT_TYPE;
+ mr = &masking_MS_ratio[gr_out][chn - 2];
+ }
+ else {
+ ppe = percep_entropy;
+ type = blocktype_d[chn];
+ mr = &masking_ratio[gr_out][chn];
+ }
+ if (type == SHORT_TYPE) {
+ ppe[chn] = pecalc_s(mr, gfc->sv_qnt.masking_lower);
+ }
+ else {
+ ppe[chn] = pecalc_l(mr, gfc->sv_qnt.masking_lower);
+ }
+
+ if (plt) {
+ plt->pe[gr_out][chn] = ppe[chn];
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * The spreading function. Values returned in units of energy
+ */
+static FLOAT
+s3_func(FLOAT bark)
+{
+ FLOAT tempx, x, tempy, temp;
+ tempx = bark;
+ if (tempx >= 0)
+ tempx *= 3;
+ else
+ tempx *= 1.5;
+
+ if (tempx >= 0.5 && tempx <= 2.5) {
+ temp = tempx - 0.5;
+ x = 8.0 * (temp * temp - 2.0 * temp);
+ }
+ else
+ x = 0.0;
+ tempx += 0.474;
+ tempy = 15.811389 + 7.5 * tempx - 17.5 * sqrt(1.0 + tempx * tempx);
+
+ if (tempy <= -60.0)
+ return 0.0;
+
+ tempx = exp((x + tempy) * LN_TO_LOG10);
+
+ /* Normalization. The spreading function should be normalized so that:
+ +inf
+ /
+ | s3 [ bark ] d(bark) = 1
+ /
+ -inf
+ */
+ tempx /= .6609193;
+ return tempx;
+}
+
+#if 0
+static FLOAT
+norm_s3_func(void)
+{
+ double lim_a = 0, lim_b = 0;
+ double x = 0, l, h;
+ for (x = 0; s3_func(x) > 1e-20; x -= 1);
+ l = x;
+ h = 0;
+ while (fabs(h - l) > 1e-12) {
+ x = (h + l) / 2;
+ if (s3_func(x) > 0) {
+ h = x;
+ }
+ else {
+ l = x;
+ }
+ }
+ lim_a = l;
+ for (x = 0; s3_func(x) > 1e-20; x += 1);
+ l = 0;
+ h = x;
+ while (fabs(h - l) > 1e-12) {
+ x = (h + l) / 2;
+ if (s3_func(x) > 0) {
+ l = x;
+ }
+ else {
+ h = x;
+ }
+ }
+ lim_b = h;
+ {
+ double sum = 0;
+ int const m = 1000;
+ int i;
+ for (i = 0; i <= m; ++i) {
+ double x = lim_a + i * (lim_b - lim_a) / m;
+ double y = s3_func(x);
+ sum += y;
+ }
+ {
+ double norm = (m + 1) / (sum * (lim_b - lim_a));
+ /*printf( "norm = %lf\n",norm); */
+ return norm;
+ }
+ }
+}
+#endif
+
+static FLOAT
+stereo_demask(double f)
+{
+ /* setup stereo demasking thresholds */
+ /* formula reverse enginerred from plot in paper */
+ double arg = freq2bark(f);
+ arg = (Min(arg, 15.5) / 15.5);
+
+ return pow(10.0, 1.25 * (1 - cos(PI * arg)) - 2.5);
+}
+
+static void
+init_numline(PsyConst_CB2SB_t * gd, FLOAT sfreq, int fft_size,
+ int mdct_size, int sbmax, int const *scalepos)
+{
+ FLOAT b_frq[CBANDS + 1];
+ FLOAT const mdct_freq_frac = sfreq / (2.0f * mdct_size);
+ FLOAT const deltafreq = fft_size / (2.0f * mdct_size);
+ int partition[HBLKSIZE] = { 0 };
+ int i, j, ni;
+ int sfb;
+ sfreq /= fft_size;
+ j = 0;
+ ni = 0;
+ /* compute numlines, the number of spectral lines in each partition band */
+ /* each partition band should be about DELBARK wide. */
+ for (i = 0; i < CBANDS; i++) {
+ FLOAT bark1;
+ int j2, nl;
+ bark1 = freq2bark(sfreq * j);
+
+ b_frq[i] = sfreq * j;
+
+ for (j2 = j; freq2bark(sfreq * j2) - bark1 < DELBARK && j2 <= fft_size / 2; j2++);
+
+ nl = j2 - j;
+ gd->numlines[i] = nl;
+ gd->rnumlines[i] = (nl > 0) ? (1.0f / nl) : 0;
+
+ ni = i + 1;
+
+ while (j < j2) {
+ assert(j < HBLKSIZE);
+ partition[j++] = i;
+ }
+ if (j > fft_size / 2) {
+ j = fft_size / 2;
+ ++i;
+ break;
+ }
+ }
+ assert(i < CBANDS);
+ b_frq[i] = sfreq * j;
+
+ gd->n_sb = sbmax;
+ gd->npart = ni;
+
+ {
+ j = 0;
+ for (i = 0; i < gd->npart; i++) {
+ int const nl = gd->numlines[i];
+ FLOAT const freq = sfreq * (j + nl / 2);
+ gd->mld_cb[i] = stereo_demask(freq);
+ j += nl;
+ }
+ for (; i < CBANDS; ++i) {
+ gd->mld_cb[i] = 1;
+ }
+ }
+ for (sfb = 0; sfb < sbmax; sfb++) {
+ int i1, i2, bo;
+ int start = scalepos[sfb];
+ int end = scalepos[sfb + 1];
+
+ i1 = floor(.5 + deltafreq * (start - .5));
+ if (i1 < 0)
+ i1 = 0;
+ i2 = floor(.5 + deltafreq * (end - .5));
+
+ if (i2 > fft_size / 2)
+ i2 = fft_size / 2;
+
+ bo = partition[i2];
+ gd->bm[sfb] = (partition[i1] + partition[i2]) / 2;
+ gd->bo[sfb] = bo;
+
+ /* calculate how much of this band belongs to current scalefactor band */
+ {
+ FLOAT const f_tmp = mdct_freq_frac * end;
+ FLOAT bo_w = (f_tmp - b_frq[bo]) / (b_frq[bo + 1] - b_frq[bo]);
+ if (bo_w < 0) {
+ bo_w = 0;
+ }
+ else {
+ if (bo_w > 1) {
+ bo_w = 1;
+ }
+ }
+ gd->bo_weight[sfb] = bo_w;
+ }
+ gd->mld[sfb] = stereo_demask(mdct_freq_frac * start);
+ }
+}
+
+static void
+compute_bark_values(PsyConst_CB2SB_t const *gd, FLOAT sfreq, int fft_size,
+ FLOAT * bval, FLOAT * bval_width)
+{
+ /* compute bark values of each critical band */
+ int k, j = 0, ni = gd->npart;
+ sfreq /= fft_size;
+ for (k = 0; k < ni; k++) {
+ int const w = gd->numlines[k];
+ FLOAT bark1, bark2;
+
+ bark1 = freq2bark(sfreq * (j));
+ bark2 = freq2bark(sfreq * (j + w - 1));
+ bval[k] = .5 * (bark1 + bark2);
+
+ bark1 = freq2bark(sfreq * (j - .5));
+ bark2 = freq2bark(sfreq * (j + w - .5));
+ bval_width[k] = bark2 - bark1;
+ j += w;
+ }
+}
+
+static int
+init_s3_values(FLOAT ** p, int (*s3ind)[2], int npart,
+ FLOAT const *bval, FLOAT const *bval_width, FLOAT const *norm)
+{
+ FLOAT s3[CBANDS][CBANDS];
+ /* The s3 array is not linear in the bark scale.
+ * bval[x] should be used to get the bark value.
+ */
+ int i, j, k;
+ int numberOfNoneZero = 0;
+
+ memset(&s3[0][0], 0, sizeof(s3));
+
+ /* s[i][j], the value of the spreading function,
+ * centered at band j (masker), for band i (maskee)
+ *
+ * i.e.: sum over j to spread into signal barkval=i
+ * NOTE: i and j are used opposite as in the ISO docs
+ */
+ for (i = 0; i < npart; i++) {
+ for (j = 0; j < npart; j++) {
+ FLOAT v = s3_func(bval[i] - bval[j]) * bval_width[j];
+ s3[i][j] = v * norm[i];
+ }
+ }
+ for (i = 0; i < npart; i++) {
+ for (j = 0; j < npart; j++) {
+ if (s3[i][j] > 0.0f)
+ break;
+ }
+ s3ind[i][0] = j;
+
+ for (j = npart - 1; j > 0; j--) {
+ if (s3[i][j] > 0.0f)
+ break;
+ }
+ s3ind[i][1] = j;
+ numberOfNoneZero += (s3ind[i][1] - s3ind[i][0] + 1);
+ }
+ *p = malloc(sizeof(FLOAT) * numberOfNoneZero);
+ if (!*p)
+ return -1;
+
+ k = 0;
+ for (i = 0; i < npart; i++)
+ for (j = s3ind[i][0]; j <= s3ind[i][1]; j++)
+ (*p)[k++] = s3[i][j];
+
+ return 0;
+}
+
+int
+psymodel_init(lame_global_flags const *gfp)
+{
+ lame_internal_flags *const gfc = gfp->internal_flags;
+ SessionConfig_t *const cfg = &gfc->cfg;
+ PsyStateVar_t *const psv = &gfc->sv_psy;
+ PsyConst_t *gd;
+ int i, j, b, sb, k;
+ FLOAT bvl_a = 13, bvl_b = 24;
+ FLOAT snr_l_a = 0, snr_l_b = 0;
+ FLOAT snr_s_a = -8.25, snr_s_b = -4.5;
+
+ FLOAT bval[CBANDS];
+ FLOAT bval_width[CBANDS];
+ FLOAT norm[CBANDS];
+ FLOAT const sfreq = cfg->samplerate_out;
+
+ FLOAT xav = 10, xbv = 12;
+ FLOAT const minval_low = (0.f - cfg->minval);
+
+ if (gfc->cd_psy != 0) {
+ return 0;
+ }
+ memset(norm, 0, sizeof(norm));
+
+ gd = calloc(1, sizeof(PsyConst_t));
+ gfc->cd_psy = gd;
+
+ gd->force_short_block_calc = gfp->experimentalZ;
+
+ psv->blocktype_old[0] = psv->blocktype_old[1] = NORM_TYPE; /* the vbr header is long blocks */
+
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < CBANDS; ++j) {
+ psv->nb_l1[i][j] = 1e20;
+ psv->nb_l2[i][j] = 1e20;
+ psv->nb_s1[i][j] = psv->nb_s2[i][j] = 1.0;
+ }
+ for (sb = 0; sb < SBMAX_l; sb++) {
+ psv->en[i].l[sb] = 1e20;
+ psv->thm[i].l[sb] = 1e20;
+ }
+ for (j = 0; j < 3; ++j) {
+ for (sb = 0; sb < SBMAX_s; sb++) {
+ psv->en[i].s[sb][j] = 1e20;
+ psv->thm[i].s[sb][j] = 1e20;
+ }
+ psv->last_attacks[i] = 0;
+ }
+ for (j = 0; j < 9; j++)
+ psv->last_en_subshort[i][j] = 10.;
+ }
+
+
+ /* init. for loudness approx. -jd 2001 mar 27 */
+ psv->loudness_sq_save[0] = psv->loudness_sq_save[1] = 0.0;
+
+
+
+ /*************************************************************************
+ * now compute the psychoacoustic model specific constants
+ ************************************************************************/
+ /* compute numlines, bo, bm, bval, bval_width, mld */
+ init_numline(&gd->l, sfreq, BLKSIZE, 576, SBMAX_l, gfc->scalefac_band.l);
+ assert(gd->l.npart < CBANDS);
+ compute_bark_values(&gd->l, sfreq, BLKSIZE, bval, bval_width);
+
+ /* compute the spreading function */
+ for (i = 0; i < gd->l.npart; i++) {
+ double snr = snr_l_a;
+ if (bval[i] >= bvl_a) {
+ snr = snr_l_b * (bval[i] - bvl_a) / (bvl_b - bvl_a)
+ + snr_l_a * (bvl_b - bval[i]) / (bvl_b - bvl_a);
+ }
+ norm[i] = pow(10.0, snr / 10.0);
+ }
+ i = init_s3_values(&gd->l.s3, gd->l.s3ind, gd->l.npart, bval, bval_width, norm);
+ if (i)
+ return i;
+
+ /* compute long block specific values, ATH and MINVAL */
+ j = 0;
+ for (i = 0; i < gd->l.npart; i++) {
+ double x;
+
+ /* ATH */
+ x = FLOAT_MAX;
+ for (k = 0; k < gd->l.numlines[i]; k++, j++) {
+ FLOAT const freq = sfreq * j / (1000.0 * BLKSIZE);
+ FLOAT level;
+ /* freq = Min(.1,freq); *//* ATH below 100 Hz constant, not further climbing */
+ level = ATHformula(cfg, freq * 1000) - 20; /* scale to FFT units; returned value is in dB */
+ level = pow(10., 0.1 * level); /* convert from dB -> energy */
+ level *= gd->l.numlines[i];
+ if (x > level)
+ x = level;
+ }
+ gfc->ATH->cb_l[i] = x;
+
+ /* MINVAL.
+ For low freq, the strength of the masking is limited by minval
+ this is an ISO MPEG1 thing, dont know if it is really needed */
+ /* FIXME: it does work to reduce low-freq problems in S53-Wind-Sax
+ and lead-voice samples, but introduces some 3 kbps bit bloat too.
+ TODO: Further refinement of the shape of this hack.
+ */
+ x = 20.0 * (bval[i] / xav - 1.0);
+ if (x > 6) {
+ x = 30;
+ }
+ if (x < minval_low) {
+ x = minval_low;
+ }
+ if (cfg->samplerate_out < 44000) {
+ x = 30;
+ }
+ x -= 8.;
+ gd->l.minval[i] = pow(10.0, x / 10.) * gd->l.numlines[i];
+ }
+
+ /************************************************************************
+ * do the same things for short blocks
+ ************************************************************************/
+ init_numline(&gd->s, sfreq, BLKSIZE_s, 192, SBMAX_s, gfc->scalefac_band.s);
+ assert(gd->s.npart < CBANDS);
+ compute_bark_values(&gd->s, sfreq, BLKSIZE_s, bval, bval_width);
+
+ /* SNR formula. short block is normalized by SNR. is it still right ? */
+ j = 0;
+ for (i = 0; i < gd->s.npart; i++) {
+ double x;
+ double snr = snr_s_a;
+ if (bval[i] >= bvl_a) {
+ snr = snr_s_b * (bval[i] - bvl_a) / (bvl_b - bvl_a)
+ + snr_s_a * (bvl_b - bval[i]) / (bvl_b - bvl_a);
+ }
+ norm[i] = pow(10.0, snr / 10.0);
+
+ /* ATH */
+ x = FLOAT_MAX;
+ for (k = 0; k < gd->s.numlines[i]; k++, j++) {
+ FLOAT const freq = sfreq * j / (1000.0 * BLKSIZE_s);
+ FLOAT level;
+ /* freq = Min(.1,freq); *//* ATH below 100 Hz constant, not further climbing */
+ level = ATHformula(cfg, freq * 1000) - 20; /* scale to FFT units; returned value is in dB */
+ level = pow(10., 0.1 * level); /* convert from dB -> energy */
+ level *= gd->s.numlines[i];
+ if (x > level)
+ x = level;
+ }
+ gfc->ATH->cb_s[i] = x;
+
+ /* MINVAL.
+ For low freq, the strength of the masking is limited by minval
+ this is an ISO MPEG1 thing, dont know if it is really needed */
+ x = 7.0 * (bval[i] / xbv - 1.0);
+ if (bval[i] > xbv) {
+ x *= 1 + log(1 + x) * 3.1;
+ }
+ if (bval[i] < xbv) {
+ x *= 1 + log(1 - x) * 2.3;
+ }
+ if (x > 6) {
+ x = 30;
+ }
+ if (x < minval_low) {
+ x = minval_low;
+ }
+ if (cfg->samplerate_out < 44000) {
+ x = 30;
+ }
+ x -= 8;
+ gd->s.minval[i] = pow(10.0, x / 10) * gd->s.numlines[i];
+ }
+
+ i = init_s3_values(&gd->s.s3, gd->s.s3ind, gd->s.npart, bval, bval_width, norm);
+ if (i)
+ return i;
+
+
+ init_mask_add_max_values();
+ init_fft(gfc);
+
+ /* setup temporal masking */
+ gd->decay = exp(-1.0 * LOG10 / (temporalmask_sustain_sec * sfreq / 192.0));
+
+ {
+ FLOAT msfix;
+ msfix = NS_MSFIX;
+ if (cfg->use_safe_joint_stereo)
+ msfix = 1.0;
+ if (fabs(cfg->msfix) > 0.0)
+ msfix = cfg->msfix;
+ cfg->msfix = msfix;
+
+ /* spread only from npart_l bands. Normally, we use the spreading
+ * function to convolve from npart_l down to npart_l bands
+ */
+ for (b = 0; b < gd->l.npart; b++)
+ if (gd->l.s3ind[b][1] > gd->l.npart - 1)
+ gd->l.s3ind[b][1] = gd->l.npart - 1;
+ }
+
+ /* prepare for ATH auto adjustment:
+ * we want to decrease the ATH by 12 dB per second
+ */
+#define frame_duration (576. * cfg->mode_gr / sfreq)
+ gfc->ATH->decay = pow(10., -12. / 10. * frame_duration);
+ gfc->ATH->adjust_factor = 0.01; /* minimum, for leading low loudness */
+ gfc->ATH->adjust_limit = 1.0; /* on lead, allow adjust up to maximum */
+#undef frame_duration
+
+ assert(gd->l.bo[SBMAX_l - 1] <= gd->l.npart);
+ assert(gd->s.bo[SBMAX_s - 1] <= gd->s.npart);
+
+ if (cfg->ATHtype != -1) {
+ /* compute equal loudness weights (eql_w) */
+ FLOAT freq;
+ FLOAT const freq_inc = (FLOAT) cfg->samplerate_out / (FLOAT) (BLKSIZE);
+ FLOAT eql_balance = 0.0;
+ freq = 0.0;
+ for (i = 0; i < BLKSIZE / 2; ++i) {
+ /* convert ATH dB to relative power (not dB) */
+ /* to determine eql_w */
+ freq += freq_inc;
+ gfc->ATH->eql_w[i] = 1. / pow(10, ATHformula(cfg, freq) / 10);
+ eql_balance += gfc->ATH->eql_w[i];
+ }
+ eql_balance = 1.0 / eql_balance;
+ for (i = BLKSIZE / 2; --i >= 0;) { /* scale weights */
+ gfc->ATH->eql_w[i] *= eql_balance;
+ }
+ }
+ {
+ for (b = j = 0; b < gd->s.npart; ++b) {
+ for (i = 0; i < gd->s.numlines[b]; ++i) {
+ ++j;
+ }
+ }
+ assert(j == 129);
+ for (b = j = 0; b < gd->l.npart; ++b) {
+ for (i = 0; i < gd->l.numlines[b]; ++i) {
+ ++j;
+ }
+ }
+ assert(j == 513);
+ }
+ /* short block attack threshold */
+ {
+ float x = gfp->attackthre;
+ float y = gfp->attackthre_s;
+ if (x < 0) {
+ x = NSATTACKTHRE;
+ }
+ if (y < 0) {
+ y = NSATTACKTHRE_S;
+ }
+ gd->attack_threshold[0] = gd->attack_threshold[1] = gd->attack_threshold[2] = x;
+ gd->attack_threshold[3] = y;
+ }
+ {
+ float sk_s = -10.f, sk_l = -4.7f;
+ static float const sk[] =
+ { -7.4, -7.4, -7.4, -9.5, -7.4, -6.1, -5.5, -4.7, -4.7, -4.7, -4.7 };
+ if (gfp->VBR_q < 4) {
+ sk_l = sk_s = sk[0];
+ }
+ else {
+ sk_l = sk_s = sk[gfp->VBR_q] + gfp->VBR_q_frac * (sk[gfp->VBR_q] - sk[gfp->VBR_q + 1]);
+ }
+ b = 0;
+ for (; b < gd->s.npart; b++) {
+ float m = (float) (gd->s.npart - b) / gd->s.npart;
+ gd->s.masking_lower[b] = powf(10.f, sk_s * m * 0.1f);
+ }
+ for (; b < CBANDS; ++b) {
+ gd->s.masking_lower[b] = 1.f;
+ }
+ b = 0;
+ for (; b < gd->l.npart; b++) {
+ float m = (float) (gd->l.npart - b) / gd->l.npart;
+ gd->l.masking_lower[b] = powf(10.f, sk_l * m * 0.1f);
+ }
+ for (; b < CBANDS; ++b) {
+ gd->l.masking_lower[b] = 1.f;
+ }
+ }
+ memcpy(&gd->l_to_s, &gd->l, sizeof(gd->l_to_s));
+ init_numline(&gd->l_to_s, sfreq, BLKSIZE, 192, SBMAX_s, gfc->scalefac_band.s);
+ return 0;
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/psymodel.h b/libnative/src/main/cpp/module/mp3/lame/psymodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..f46083cd832fd02829c577c5bbd021536d817761
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/psymodel.h
@@ -0,0 +1,64 @@
+/*
+ * psymodel.h
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_PSYMODEL_H
+#define LAME_PSYMODEL_H
+
+
+int L3psycho_anal_ns(lame_internal_flags * gfc,
+ const sample_t *const buffer[2], int gr,
+ III_psy_ratio ratio[2][2],
+ III_psy_ratio MS_ratio[2][2],
+ FLOAT pe[2], FLOAT pe_MS[2], FLOAT ener[2], int blocktype_d[2]);
+
+int L3psycho_anal_vbr(lame_internal_flags * gfc,
+ const sample_t *const buffer[2], int gr,
+ III_psy_ratio ratio[2][2],
+ III_psy_ratio MS_ratio[2][2],
+ FLOAT pe[2], FLOAT pe_MS[2], FLOAT ener[2], int blocktype_d[2]);
+
+
+int psymodel_init(lame_global_flags const* gfp);
+
+
+#define rpelev 2
+#define rpelev2 16
+#define rpelev_s 2
+#define rpelev2_s 16
+
+/* size of each partition band, in barks: */
+#define DELBARK .34
+
+
+/* tuned for output level (sensitive to energy scale) */
+#define VO_SCALE (1./( 14752*14752 )/(BLKSIZE/2))
+
+#define temporalmask_sustain_sec 0.01
+
+#define NS_PREECHO_ATT0 0.8
+#define NS_PREECHO_ATT1 0.6
+#define NS_PREECHO_ATT2 0.3
+
+#define NS_MSFIX 3.5
+#define NSATTACKTHRE 4.4
+#define NSATTACKTHRE_S 25
+
+#endif /* LAME_PSYMODEL_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/quantize.c b/libnative/src/main/cpp/module/mp3/lame/quantize.c
new file mode 100644
index 0000000000000000000000000000000000000000..ffbc6bd011ce208c52972bdafe1db2e81847ffa5
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/quantize.c
@@ -0,0 +1,2050 @@
+/*
+ * MP3 quantization
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 1999-2003 Takehiro Tominaga
+ * Copyright (c) 2000-2011 Robert Hegemann
+ * Copyright (c) 2001-2005 Gabriel Bouvigne
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: quantize.c,v 1.216.2.1 2012/01/08 23:49:58 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "quantize_pvt.h"
+#include "reservoir.h"
+#include "bitstream.h"
+#include "vbrquantize.h"
+#include "quantize.h"
+#ifdef HAVE_XMMINTRIN_H
+#include "vector/lame_intrin.h"
+#endif
+
+
+
+
+/* convert from L/R <-> Mid/Side */
+static void
+ms_convert(III_side_info_t * l3_side, int gr)
+{
+ int i;
+ for (i = 0; i < 576; ++i) {
+ FLOAT l, r;
+ l = l3_side->tt[gr][0].xr[i];
+ r = l3_side->tt[gr][1].xr[i];
+ l3_side->tt[gr][0].xr[i] = (l + r) * (FLOAT) (SQRT2 * 0.5);
+ l3_side->tt[gr][1].xr[i] = (l - r) * (FLOAT) (SQRT2 * 0.5);
+ }
+}
+
+/************************************************************************
+ *
+ * init_outer_loop()
+ * mt 6/99
+ *
+ * initializes cod_info, scalefac and xrpow
+ *
+ * returns 0 if all energies in xr are zero, else 1
+ *
+ ************************************************************************/
+
+static void
+init_xrpow_core_c(gr_info * const cod_info, FLOAT xrpow[576], int upper, FLOAT * sum)
+{
+ int i;
+ FLOAT tmp;
+ *sum = 0;
+ for (i = 0; i <= upper; ++i) {
+ tmp = fabs(cod_info->xr[i]);
+ *sum += tmp;
+ xrpow[i] = sqrt(tmp * sqrt(tmp));
+
+ if (xrpow[i] > cod_info->xrpow_max)
+ cod_info->xrpow_max = xrpow[i];
+ }
+}
+
+
+
+
+
+void
+init_xrpow_core_init(lame_internal_flags * const gfc)
+{
+ gfc->init_xrpow_core = init_xrpow_core_c;
+
+#if defined(HAVE_XMMINTRIN_H)
+ if (gfc->CPU_features.SSE)
+ gfc->init_xrpow_core = init_xrpow_core_sse;
+#endif
+#ifndef HAVE_NASM
+#ifdef MIN_ARCH_SSE
+ gfc->init_xrpow_core = init_xrpow_core_sse;
+#endif
+#endif
+}
+
+
+
+static int
+init_xrpow(lame_internal_flags * gfc, gr_info * const cod_info, FLOAT xrpow[576])
+{
+ FLOAT sum = 0;
+ int i;
+ int const upper = cod_info->max_nonzero_coeff;
+
+ assert(xrpow != NULL);
+ cod_info->xrpow_max = 0;
+
+ /* check if there is some energy we have to quantize
+ * and calculate xrpow matching our fresh scalefactors
+ */
+ assert(0 <= upper && upper <= 575);
+ memset(&(xrpow[upper]), 0, (576 - upper) * sizeof(xrpow[0]));
+
+
+ gfc->init_xrpow_core(cod_info, xrpow, upper, &sum);
+
+ /* return 1 if we have something to quantize, else 0
+ */
+ if (sum > (FLOAT) 1E-20) {
+ int j = 0;
+ if (gfc->sv_qnt.substep_shaping & 2)
+ j = 1;
+
+ for (i = 0; i < cod_info->psymax; i++)
+ gfc->sv_qnt.pseudohalf[i] = j;
+
+ return 1;
+ }
+
+ memset(&cod_info->l3_enc[0], 0, sizeof(int) * 576);
+ return 0;
+}
+
+
+
+
+
+/*
+Gabriel Bouvigne feb/apr 2003
+Analog silence detection in partitionned sfb21
+or sfb12 for short blocks
+
+From top to bottom of sfb, changes to 0
+coeffs which are below ath. It stops on the first
+coeff higher than ath.
+*/
+static void
+psfb21_analogsilence(lame_internal_flags const *gfc, gr_info * const cod_info)
+{
+ ATH_t const *const ATH = gfc->ATH;
+ FLOAT *const xr = cod_info->xr;
+
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type, but not SHORT blocks */
+ int gsfb;
+ int stop = 0;
+ for (gsfb = PSFB21 - 1; gsfb >= 0 && !stop; gsfb--) {
+ int const start = gfc->scalefac_band.psfb21[gsfb];
+ int const end = gfc->scalefac_band.psfb21[gsfb + 1];
+ int j;
+ FLOAT ath21;
+ ath21 = athAdjust(ATH->adjust_factor, ATH->psfb21[gsfb], ATH->floor, 0);
+
+ if (gfc->sv_qnt.longfact[21] > 1e-12f)
+ ath21 *= gfc->sv_qnt.longfact[21];
+
+ for (j = end - 1; j >= start; j--) {
+ if (fabs(xr[j]) < ath21)
+ xr[j] = 0;
+ else {
+ stop = 1;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /*note: short blocks coeffs are reordered */
+ int block;
+ for (block = 0; block < 3; block++) {
+
+ int gsfb;
+ int stop = 0;
+ for (gsfb = PSFB12 - 1; gsfb >= 0 && !stop; gsfb--) {
+ int const start = gfc->scalefac_band.s[12] * 3 +
+ (gfc->scalefac_band.s[13] - gfc->scalefac_band.s[12]) * block +
+ (gfc->scalefac_band.psfb12[gsfb] - gfc->scalefac_band.psfb12[0]);
+ int const end =
+ start + (gfc->scalefac_band.psfb12[gsfb + 1] - gfc->scalefac_band.psfb12[gsfb]);
+ int j;
+ FLOAT ath12;
+ ath12 = athAdjust(ATH->adjust_factor, ATH->psfb12[gsfb], ATH->floor, 0);
+
+ if (gfc->sv_qnt.shortfact[12] > 1e-12f)
+ ath12 *= gfc->sv_qnt.shortfact[12];
+
+ for (j = end - 1; j >= start; j--) {
+ if (fabs(xr[j]) < ath12)
+ xr[j] = 0;
+ else {
+ stop = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+
+
+
+static void
+init_outer_loop(lame_internal_flags const *gfc, gr_info * const cod_info)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int sfb, j;
+ /* initialize fresh cod_info
+ */
+ cod_info->part2_3_length = 0;
+ cod_info->big_values = 0;
+ cod_info->count1 = 0;
+ cod_info->global_gain = 210;
+ cod_info->scalefac_compress = 0;
+ /* mixed_block_flag, block_type was set in psymodel.c */
+ cod_info->table_select[0] = 0;
+ cod_info->table_select[1] = 0;
+ cod_info->table_select[2] = 0;
+ cod_info->subblock_gain[0] = 0;
+ cod_info->subblock_gain[1] = 0;
+ cod_info->subblock_gain[2] = 0;
+ cod_info->subblock_gain[3] = 0; /* this one is always 0 */
+ cod_info->region0_count = 0;
+ cod_info->region1_count = 0;
+ cod_info->preflag = 0;
+ cod_info->scalefac_scale = 0;
+ cod_info->count1table_select = 0;
+ cod_info->part2_length = 0;
+ if (cfg->samplerate_out <= 8000) {
+ cod_info->sfb_lmax = 17;
+ cod_info->sfb_smin = 9;
+ cod_info->psy_lmax = 17;
+ }
+ else {
+ cod_info->sfb_lmax = SBPSY_l;
+ cod_info->sfb_smin = SBPSY_s;
+ cod_info->psy_lmax = gfc->sv_qnt.sfb21_extra ? SBMAX_l : SBPSY_l;
+ }
+ cod_info->psymax = cod_info->psy_lmax;
+ cod_info->sfbmax = cod_info->sfb_lmax;
+ cod_info->sfbdivide = 11;
+ for (sfb = 0; sfb < SBMAX_l; sfb++) {
+ cod_info->width[sfb]
+ = gfc->scalefac_band.l[sfb + 1] - gfc->scalefac_band.l[sfb];
+ cod_info->window[sfb] = 3; /* which is always 0. */
+ }
+ if (cod_info->block_type == SHORT_TYPE) {
+ FLOAT ixwork[576];
+ FLOAT *ix;
+
+ cod_info->sfb_smin = 0;
+ cod_info->sfb_lmax = 0;
+ if (cod_info->mixed_block_flag) {
+ /*
+ * MPEG-1: sfbs 0-7 long block, 3-12 short blocks
+ * MPEG-2(.5): sfbs 0-5 long block, 3-12 short blocks
+ */
+ cod_info->sfb_smin = 3;
+ cod_info->sfb_lmax = cfg->mode_gr * 2 + 4;
+ }
+ if (cfg->samplerate_out <= 8000) {
+ cod_info->psymax
+ = cod_info->sfb_lmax
+ + 3 * (9 - cod_info->sfb_smin);
+ cod_info->sfbmax = cod_info->sfb_lmax + 3 * (9 - cod_info->sfb_smin);
+ }
+ else {
+ cod_info->psymax
+ = cod_info->sfb_lmax
+ + 3 * ((gfc->sv_qnt.sfb21_extra ? SBMAX_s : SBPSY_s) - cod_info->sfb_smin);
+ cod_info->sfbmax = cod_info->sfb_lmax + 3 * (SBPSY_s - cod_info->sfb_smin);
+ }
+ cod_info->sfbdivide = cod_info->sfbmax - 18;
+ cod_info->psy_lmax = cod_info->sfb_lmax;
+ /* re-order the short blocks, for more efficient encoding below */
+ /* By Takehiro TOMINAGA */
+ /*
+ Within each scalefactor band, data is given for successive
+ time windows, beginning with window 0 and ending with window 2.
+ Within each window, the quantized values are then arranged in
+ order of increasing frequency...
+ */
+ ix = &cod_info->xr[gfc->scalefac_band.l[cod_info->sfb_lmax]];
+ memcpy(ixwork, cod_info->xr, 576 * sizeof(FLOAT));
+ for (sfb = cod_info->sfb_smin; sfb < SBMAX_s; sfb++) {
+ int const start = gfc->scalefac_band.s[sfb];
+ int const end = gfc->scalefac_band.s[sfb + 1];
+ int window, l;
+ for (window = 0; window < 3; window++) {
+ for (l = start; l < end; l++) {
+ *ix++ = ixwork[3 * l + window];
+ }
+ }
+ }
+
+ j = cod_info->sfb_lmax;
+ for (sfb = cod_info->sfb_smin; sfb < SBMAX_s; sfb++) {
+ cod_info->width[j] = cod_info->width[j + 1] = cod_info->width[j + 2]
+ = gfc->scalefac_band.s[sfb + 1] - gfc->scalefac_band.s[sfb];
+ cod_info->window[j] = 0;
+ cod_info->window[j + 1] = 1;
+ cod_info->window[j + 2] = 2;
+ j += 3;
+ }
+ }
+
+ cod_info->count1bits = 0;
+ cod_info->sfb_partition_table = nr_of_sfb_block[0][0];
+ cod_info->slen[0] = 0;
+ cod_info->slen[1] = 0;
+ cod_info->slen[2] = 0;
+ cod_info->slen[3] = 0;
+
+ cod_info->max_nonzero_coeff = 575;
+
+ /* fresh scalefactors are all zero
+ */
+ memset(cod_info->scalefac, 0, sizeof(cod_info->scalefac));
+
+ if (cfg->vbr != vbr_mt && cfg->vbr != vbr_mtrh && cfg->vbr != vbr_abr && cfg->vbr != vbr_off) {
+ psfb21_analogsilence(gfc, cod_info);
+ }
+}
+
+
+
+/************************************************************************
+ *
+ * bin_search_StepSize()
+ *
+ * author/date??
+ *
+ * binary step size search
+ * used by outer_loop to get a quantizer step size to start with
+ *
+ ************************************************************************/
+
+typedef enum {
+ BINSEARCH_NONE,
+ BINSEARCH_UP,
+ BINSEARCH_DOWN
+} binsearchDirection_t;
+
+static int
+bin_search_StepSize(lame_internal_flags * const gfc, gr_info * const cod_info,
+ int desired_rate, const int ch, const FLOAT xrpow[576])
+{
+ int nBits;
+ int CurrentStep = gfc->sv_qnt.CurrentStep[ch];
+ int flag_GoneOver = 0;
+ int const start = gfc->sv_qnt.OldValue[ch];
+ binsearchDirection_t Direction = BINSEARCH_NONE;
+ cod_info->global_gain = start;
+ desired_rate -= cod_info->part2_length;
+
+ assert(CurrentStep);
+ for (;;) {
+ int step;
+ nBits = count_bits(gfc, xrpow, cod_info, 0);
+
+ if (CurrentStep == 1 || nBits == desired_rate)
+ break; /* nothing to adjust anymore */
+
+ if (nBits > desired_rate) {
+ /* increase Quantize_StepSize */
+ if (Direction == BINSEARCH_DOWN)
+ flag_GoneOver = 1;
+
+ if (flag_GoneOver)
+ CurrentStep /= 2;
+ Direction = BINSEARCH_UP;
+ step = CurrentStep;
+ }
+ else {
+ /* decrease Quantize_StepSize */
+ if (Direction == BINSEARCH_UP)
+ flag_GoneOver = 1;
+
+ if (flag_GoneOver)
+ CurrentStep /= 2;
+ Direction = BINSEARCH_DOWN;
+ step = -CurrentStep;
+ }
+ cod_info->global_gain += step;
+ if (cod_info->global_gain < 0) {
+ cod_info->global_gain = 0;
+ flag_GoneOver = 1;
+ }
+ if (cod_info->global_gain > 255) {
+ cod_info->global_gain = 255;
+ flag_GoneOver = 1;
+ }
+ }
+
+ assert(cod_info->global_gain >= 0);
+ assert(cod_info->global_gain < 256);
+
+ while (nBits > desired_rate && cod_info->global_gain < 255) {
+ cod_info->global_gain++;
+ nBits = count_bits(gfc, xrpow, cod_info, 0);
+ }
+ gfc->sv_qnt.CurrentStep[ch] = (start - cod_info->global_gain >= 4) ? 4 : 2;
+ gfc->sv_qnt.OldValue[ch] = cod_info->global_gain;
+ cod_info->part2_3_length = nBits;
+ return nBits;
+}
+
+
+
+
+/************************************************************************
+ *
+ * trancate_smallspectrums()
+ *
+ * Takehiro TOMINAGA 2002-07-21
+ *
+ * trancate smaller nubmers into 0 as long as the noise threshold is allowed.
+ *
+ ************************************************************************/
+static int
+floatcompare(const void *v1, const void *v2)
+{
+ const FLOAT *const a = v1, *const b = v2;
+ if (*a > *b)
+ return 1;
+ if (*a < *b)
+ return -1;
+ return 0;
+}
+
+static void
+trancate_smallspectrums(lame_internal_flags const *gfc,
+ gr_info * const gi, const FLOAT * const l3_xmin, FLOAT * const work)
+{
+ int sfb, j, width;
+ FLOAT distort[SFBMAX];
+ calc_noise_result dummy;
+
+ if ((!(gfc->sv_qnt.substep_shaping & 4) && gi->block_type == SHORT_TYPE)
+ || gfc->sv_qnt.substep_shaping & 0x80)
+ return;
+ (void) calc_noise(gi, l3_xmin, distort, &dummy, 0);
+ for (j = 0; j < 576; j++) {
+ FLOAT xr = 0.0;
+ if (gi->l3_enc[j] != 0)
+ xr = fabs(gi->xr[j]);
+ work[j] = xr;
+ }
+
+ j = 0;
+ sfb = 8;
+ if (gi->block_type == SHORT_TYPE)
+ sfb = 6;
+ do {
+ FLOAT allowedNoise, trancateThreshold;
+ int nsame, start;
+
+ width = gi->width[sfb];
+ j += width;
+ if (distort[sfb] >= 1.0)
+ continue;
+
+ qsort(&work[j - width], width, sizeof(FLOAT), floatcompare);
+ if (EQ(work[j - 1], 0.0))
+ continue; /* all zero sfb */
+
+ allowedNoise = (1.0 - distort[sfb]) * l3_xmin[sfb];
+ trancateThreshold = 0.0;
+ start = 0;
+ do {
+ FLOAT noise;
+ for (nsame = 1; start + nsame < width; nsame++)
+ if (NEQ(work[start + j - width], work[start + j + nsame - width]))
+ break;
+
+ noise = work[start + j - width] * work[start + j - width] * nsame;
+ if (allowedNoise < noise) {
+ if (start != 0)
+ trancateThreshold = work[start + j - width - 1];
+ break;
+ }
+ allowedNoise -= noise;
+ start += nsame;
+ } while (start < width);
+ if (EQ(trancateThreshold, 0.0))
+ continue;
+
+/* printf("%e %e %e\n", */
+/* trancateThreshold/l3_xmin[sfb], */
+/* trancateThreshold/(l3_xmin[sfb]*start), */
+/* trancateThreshold/(l3_xmin[sfb]*(start+width)) */
+/* ); */
+/* if (trancateThreshold > 1000*l3_xmin[sfb]*start) */
+/* trancateThreshold = 1000*l3_xmin[sfb]*start; */
+
+ do {
+ if (fabs(gi->xr[j - width]) <= trancateThreshold)
+ gi->l3_enc[j - width] = 0;
+ } while (--width > 0);
+ } while (++sfb < gi->psymax);
+
+ gi->part2_3_length = noquant_count_bits(gfc, gi, 0);
+}
+
+
+/*************************************************************************
+ *
+ * loop_break()
+ *
+ * author/date??
+ *
+ * Function: Returns zero if there is a scalefac which has not been
+ * amplified. Otherwise it returns one.
+ *
+ *************************************************************************/
+
+inline static int
+loop_break(const gr_info * const cod_info)
+{
+ int sfb;
+
+ for (sfb = 0; sfb < cod_info->sfbmax; sfb++)
+ if (cod_info->scalefac[sfb]
+ + cod_info->subblock_gain[cod_info->window[sfb]] == 0)
+ return 0;
+
+ return 1;
+}
+
+
+
+
+/* mt 5/99: Function: Improved calc_noise for a single channel */
+
+/*************************************************************************
+ *
+ * quant_compare()
+ *
+ * author/date??
+ *
+ * several different codes to decide which quantization is better
+ *
+ *************************************************************************/
+
+static double
+penalties(double noise)
+{
+ return FAST_LOG10(0.368 + 0.632 * noise * noise * noise);
+}
+
+static double
+get_klemm_noise(const FLOAT * distort, const gr_info * const gi)
+{
+ int sfb;
+ double klemm_noise = 1E-37;
+ for (sfb = 0; sfb < gi->psymax; sfb++)
+ klemm_noise += penalties(distort[sfb]);
+
+ return Max(1e-20, klemm_noise);
+}
+
+inline static int
+quant_compare(const int quant_comp,
+ const calc_noise_result * const best,
+ calc_noise_result * const calc, const gr_info * const gi, const FLOAT * distort)
+{
+ /*
+ noise is given in decibels (dB) relative to masking thesholds.
+
+ over_noise: ??? (the previous comment is fully wrong)
+ tot_noise: ??? (the previous comment is fully wrong)
+ max_noise: max quantization noise
+
+ */
+ int better;
+
+ switch (quant_comp) {
+ default:
+ case 9:{
+ if (best->over_count > 0) {
+ /* there are distorted sfb */
+ better = calc->over_SSD <= best->over_SSD;
+ if (calc->over_SSD == best->over_SSD)
+ better = calc->bits < best->bits;
+ }
+ else {
+ /* no distorted sfb */
+ better = ((calc->max_noise < 0) &&
+ ((calc->max_noise * 10 + calc->bits) <=
+ (best->max_noise * 10 + best->bits)));
+ }
+ break;
+ }
+
+ case 0:
+ better = calc->over_count < best->over_count
+ || (calc->over_count == best->over_count && calc->over_noise < best->over_noise)
+ || (calc->over_count == best->over_count &&
+ EQ(calc->over_noise, best->over_noise) && calc->tot_noise < best->tot_noise);
+ break;
+
+ case 8:
+ calc->max_noise = get_klemm_noise(distort, gi);
+ /*lint --fallthrough */
+ case 1:
+ better = calc->max_noise < best->max_noise;
+ break;
+ case 2:
+ better = calc->tot_noise < best->tot_noise;
+ break;
+ case 3:
+ better = (calc->tot_noise < best->tot_noise)
+ && (calc->max_noise < best->max_noise);
+ break;
+ case 4:
+ better = (calc->max_noise <= 0.0 && best->max_noise > 0.2)
+ || (calc->max_noise <= 0.0 &&
+ best->max_noise < 0.0 &&
+ best->max_noise > calc->max_noise - 0.2 && calc->tot_noise < best->tot_noise)
+ || (calc->max_noise <= 0.0 &&
+ best->max_noise > 0.0 &&
+ best->max_noise > calc->max_noise - 0.2 &&
+ calc->tot_noise < best->tot_noise + best->over_noise)
+ || (calc->max_noise > 0.0 &&
+ best->max_noise > -0.05 &&
+ best->max_noise > calc->max_noise - 0.1 &&
+ calc->tot_noise + calc->over_noise < best->tot_noise + best->over_noise)
+ || (calc->max_noise > 0.0 &&
+ best->max_noise > -0.1 &&
+ best->max_noise > calc->max_noise - 0.15 &&
+ calc->tot_noise + calc->over_noise + calc->over_noise <
+ best->tot_noise + best->over_noise + best->over_noise);
+ break;
+ case 5:
+ better = calc->over_noise < best->over_noise
+ || (EQ(calc->over_noise, best->over_noise) && calc->tot_noise < best->tot_noise);
+ break;
+ case 6:
+ better = calc->over_noise < best->over_noise
+ || (EQ(calc->over_noise, best->over_noise) &&
+ (calc->max_noise < best->max_noise
+ || (EQ(calc->max_noise, best->max_noise) && calc->tot_noise <= best->tot_noise)
+ ));
+ break;
+ case 7:
+ better = calc->over_count < best->over_count || calc->over_noise < best->over_noise;
+ break;
+ }
+
+
+ if (best->over_count == 0) {
+ /*
+ If no distorted bands, only use this quantization
+ if it is better, and if it uses less bits.
+ Unfortunately, part2_3_length is sometimes a poor
+ estimator of the final size at low bitrates.
+ */
+ better = better && calc->bits < best->bits;
+ }
+
+
+ return better;
+}
+
+
+
+/*************************************************************************
+ *
+ * amp_scalefac_bands()
+ *
+ * author/date??
+ *
+ * Amplify the scalefactor bands that violate the masking threshold.
+ * See ISO 11172-3 Section C.1.5.4.3.5
+ *
+ * distort[] = noise/masking
+ * distort[] > 1 ==> noise is not masked
+ * distort[] < 1 ==> noise is masked
+ * max_dist = maximum value of distort[]
+ *
+ * Three algorithms:
+ * noise_shaping_amp
+ * 0 Amplify all bands with distort[]>1.
+ *
+ * 1 Amplify all bands with distort[] >= max_dist^(.5);
+ * ( 50% in the db scale)
+ *
+ * 2 Amplify first band with distort[] >= max_dist;
+ *
+ *
+ * For algorithms 0 and 1, if max_dist < 1, then amplify all bands
+ * with distort[] >= .95*max_dist. This is to make sure we always
+ * amplify at least one band.
+ *
+ *
+ *************************************************************************/
+static void
+amp_scalefac_bands(lame_internal_flags * gfc,
+ gr_info * const cod_info, FLOAT const *distort, FLOAT xrpow[576], int bRefine)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int j, sfb;
+ FLOAT ifqstep34, trigger;
+ int noise_shaping_amp;
+
+ if (cod_info->scalefac_scale == 0) {
+ ifqstep34 = 1.29683955465100964055; /* 2**(.75*.5) */
+ }
+ else {
+ ifqstep34 = 1.68179283050742922612; /* 2**(.75*1) */
+ }
+
+ /* compute maximum value of distort[] */
+ trigger = 0;
+ for (sfb = 0; sfb < cod_info->sfbmax; sfb++) {
+ if (trigger < distort[sfb])
+ trigger = distort[sfb];
+ }
+
+ noise_shaping_amp = cfg->noise_shaping_amp;
+ if (noise_shaping_amp == 3) {
+ if (bRefine == 1)
+ noise_shaping_amp = 2;
+ else
+ noise_shaping_amp = 1;
+ }
+ switch (noise_shaping_amp) {
+ case 2:
+ /* amplify exactly 1 band */
+ break;
+
+ case 1:
+ /* amplify bands within 50% of max (on db scale) */
+ if (trigger > 1.0)
+ trigger = pow(trigger, .5);
+ else
+ trigger *= .95;
+ break;
+
+ case 0:
+ default:
+ /* ISO algorithm. amplify all bands with distort>1 */
+ if (trigger > 1.0)
+ trigger = 1.0;
+ else
+ trigger *= .95;
+ break;
+ }
+
+ j = 0;
+ for (sfb = 0; sfb < cod_info->sfbmax; sfb++) {
+ int const width = cod_info->width[sfb];
+ int l;
+ j += width;
+ if (distort[sfb] < trigger)
+ continue;
+
+ if (gfc->sv_qnt.substep_shaping & 2) {
+ gfc->sv_qnt.pseudohalf[sfb] = !gfc->sv_qnt.pseudohalf[sfb];
+ if (!gfc->sv_qnt.pseudohalf[sfb] && cfg->noise_shaping_amp == 2)
+ return;
+ }
+ cod_info->scalefac[sfb]++;
+ for (l = -width; l < 0; l++) {
+ xrpow[j + l] *= ifqstep34;
+ if (xrpow[j + l] > cod_info->xrpow_max)
+ cod_info->xrpow_max = xrpow[j + l];
+ }
+
+ if (cfg->noise_shaping_amp == 2)
+ return;
+ }
+}
+
+/*************************************************************************
+ *
+ * inc_scalefac_scale()
+ *
+ * Takehiro Tominaga 2000-xx-xx
+ *
+ * turns on scalefac scale and adjusts scalefactors
+ *
+ *************************************************************************/
+
+static void
+inc_scalefac_scale(gr_info * const cod_info, FLOAT xrpow[576])
+{
+ int l, j, sfb;
+ const FLOAT ifqstep34 = 1.29683955465100964055;
+
+ j = 0;
+ for (sfb = 0; sfb < cod_info->sfbmax; sfb++) {
+ int const width = cod_info->width[sfb];
+ int s = cod_info->scalefac[sfb];
+ if (cod_info->preflag)
+ s += pretab[sfb];
+ j += width;
+ if (s & 1) {
+ s++;
+ for (l = -width; l < 0; l++) {
+ xrpow[j + l] *= ifqstep34;
+ if (xrpow[j + l] > cod_info->xrpow_max)
+ cod_info->xrpow_max = xrpow[j + l];
+ }
+ }
+ cod_info->scalefac[sfb] = s >> 1;
+ }
+ cod_info->preflag = 0;
+ cod_info->scalefac_scale = 1;
+}
+
+
+
+/*************************************************************************
+ *
+ * inc_subblock_gain()
+ *
+ * Takehiro Tominaga 2000-xx-xx
+ *
+ * increases the subblock gain and adjusts scalefactors
+ *
+ *************************************************************************/
+
+static int
+inc_subblock_gain(const lame_internal_flags * const gfc, gr_info * const cod_info, FLOAT xrpow[576])
+{
+ int sfb, window;
+ int *const scalefac = cod_info->scalefac;
+
+ /* subbloc_gain can't do anything in the long block region */
+ for (sfb = 0; sfb < cod_info->sfb_lmax; sfb++) {
+ if (scalefac[sfb] >= 16)
+ return 1;
+ }
+
+ for (window = 0; window < 3; window++) {
+ int s1, s2, l, j;
+ s1 = s2 = 0;
+
+ for (sfb = cod_info->sfb_lmax + window; sfb < cod_info->sfbdivide; sfb += 3) {
+ if (s1 < scalefac[sfb])
+ s1 = scalefac[sfb];
+ }
+ for (; sfb < cod_info->sfbmax; sfb += 3) {
+ if (s2 < scalefac[sfb])
+ s2 = scalefac[sfb];
+ }
+
+ if (s1 < 16 && s2 < 8)
+ continue;
+
+ if (cod_info->subblock_gain[window] >= 7)
+ return 1;
+
+ /* even though there is no scalefactor for sfb12
+ * subblock gain affects upper frequencies too, that's why
+ * we have to go up to SBMAX_s
+ */
+ cod_info->subblock_gain[window]++;
+ j = gfc->scalefac_band.l[cod_info->sfb_lmax];
+ for (sfb = cod_info->sfb_lmax + window; sfb < cod_info->sfbmax; sfb += 3) {
+ FLOAT amp;
+ int const width = cod_info->width[sfb];
+ int s = scalefac[sfb];
+ assert(s >= 0);
+ s = s - (4 >> cod_info->scalefac_scale);
+ if (s >= 0) {
+ scalefac[sfb] = s;
+ j += width * 3;
+ continue;
+ }
+
+ scalefac[sfb] = 0;
+ {
+ int const gain = 210 + (s << (cod_info->scalefac_scale + 1));
+ amp = IPOW20(gain);
+ }
+ j += width * (window + 1);
+ for (l = -width; l < 0; l++) {
+ xrpow[j + l] *= amp;
+ if (xrpow[j + l] > cod_info->xrpow_max)
+ cod_info->xrpow_max = xrpow[j + l];
+ }
+ j += width * (3 - window - 1);
+ }
+
+ {
+ FLOAT const amp = IPOW20(202);
+ j += cod_info->width[sfb] * (window + 1);
+ for (l = -cod_info->width[sfb]; l < 0; l++) {
+ xrpow[j + l] *= amp;
+ if (xrpow[j + l] > cod_info->xrpow_max)
+ cod_info->xrpow_max = xrpow[j + l];
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+/********************************************************************
+ *
+ * balance_noise()
+ *
+ * Takehiro Tominaga /date??
+ * Robert Hegemann 2000-09-06: made a function of it
+ *
+ * amplifies scalefactor bands,
+ * - if all are already amplified returns 0
+ * - if some bands are amplified too much:
+ * * try to increase scalefac_scale
+ * * if already scalefac_scale was set
+ * try on short blocks to increase subblock gain
+ *
+ ********************************************************************/
+inline static int
+balance_noise(lame_internal_flags * gfc,
+ gr_info * const cod_info, FLOAT const *distort, FLOAT xrpow[576], int bRefine)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int status;
+
+ amp_scalefac_bands(gfc, cod_info, distort, xrpow, bRefine);
+
+ /* check to make sure we have not amplified too much
+ * loop_break returns 0 if there is an unamplified scalefac
+ * scale_bitcount returns 0 if no scalefactors are too large
+ */
+
+ status = loop_break(cod_info);
+
+ if (status)
+ return 0; /* all bands amplified */
+
+ /* not all scalefactors have been amplified. so these
+ * scalefacs are possibly valid. encode them:
+ */
+ status = scale_bitcount(gfc, cod_info);
+
+ if (!status)
+ return 1; /* amplified some bands not exceeding limits */
+
+ /* some scalefactors are too large.
+ * lets try setting scalefac_scale=1
+ */
+ if (cfg->noise_shaping > 1) {
+ memset(&gfc->sv_qnt.pseudohalf[0], 0, sizeof(gfc->sv_qnt.pseudohalf));
+ if (!cod_info->scalefac_scale) {
+ inc_scalefac_scale(cod_info, xrpow);
+ status = 0;
+ }
+ else {
+ if (cod_info->block_type == SHORT_TYPE && cfg->subblock_gain > 0) {
+ status = inc_subblock_gain(gfc, cod_info, xrpow)
+ || loop_break(cod_info);
+ }
+ }
+ }
+
+ if (!status) {
+ status = scale_bitcount(gfc, cod_info);
+ }
+ return !status;
+}
+
+
+
+/************************************************************************
+ *
+ * outer_loop ()
+ *
+ * Function: The outer iteration loop controls the masking conditions
+ * of all scalefactorbands. It computes the best scalefac and
+ * global gain. This module calls the inner iteration loop
+ *
+ * mt 5/99 completely rewritten to allow for bit reservoir control,
+ * mid/side channels with L/R or mid/side masking thresholds,
+ * and chooses best quantization instead of last quantization when
+ * no distortion free quantization can be found.
+ *
+ * added VBR support mt 5/99
+ *
+ * some code shuffle rh 9/00
+ ************************************************************************/
+
+static int
+outer_loop(lame_internal_flags * gfc, gr_info * const cod_info, const FLOAT * const l3_xmin, /* allowed distortion */
+ FLOAT xrpow[576], /* coloured magnitudes of spectral */
+ const int ch, const int targ_bits)
+{ /* maximum allowed bits */
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ gr_info cod_info_w;
+ FLOAT save_xrpow[576];
+ FLOAT distort[SFBMAX];
+ calc_noise_result best_noise_info;
+ int huff_bits;
+ int better;
+ int age;
+ calc_noise_data prev_noise;
+ int best_part2_3_length = 9999999;
+ int bEndOfSearch = 0;
+ int bRefine = 0;
+ int best_ggain_pass1 = 0;
+
+ (void) bin_search_StepSize(gfc, cod_info, targ_bits, ch, xrpow);
+
+ if (!cfg->noise_shaping)
+ /* fast mode, no noise shaping, we are ready */
+ return 100; /* default noise_info.over_count */
+
+ memset(&prev_noise, 0, sizeof(calc_noise_data));
+
+
+ /* compute the distortion in this quantization */
+ /* coefficients and thresholds both l/r (or both mid/side) */
+ (void) calc_noise(cod_info, l3_xmin, distort, &best_noise_info, &prev_noise);
+ best_noise_info.bits = cod_info->part2_3_length;
+
+ cod_info_w = *cod_info;
+ age = 0;
+ /* if (cfg->vbr == vbr_rh || cfg->vbr == vbr_mtrh) */
+ memcpy(save_xrpow, xrpow, sizeof(FLOAT) * 576);
+
+ while (!bEndOfSearch) {
+ /* BEGIN MAIN LOOP */
+ do {
+ calc_noise_result noise_info;
+ int search_limit;
+ int maxggain = 255;
+
+ /* When quantization with no distorted bands is found,
+ * allow up to X new unsuccesful tries in serial. This
+ * gives us more possibilities for different quant_compare modes.
+ * Much more than 3 makes not a big difference, it is only slower.
+ */
+
+ if (gfc->sv_qnt.substep_shaping & 2) {
+ search_limit = 20;
+ }
+ else {
+ search_limit = 3;
+ }
+
+
+
+ /* Check if the last scalefactor band is distorted.
+ * in VBR mode we can't get rid of the distortion, so quit now
+ * and VBR mode will try again with more bits.
+ * (makes a 10% speed increase, the files I tested were
+ * binary identical, 2000/05/20 Robert Hegemann)
+ * distort[] > 1 means noise > allowed noise
+ */
+ if (gfc->sv_qnt.sfb21_extra) {
+ if (distort[cod_info_w.sfbmax] > 1.0)
+ break;
+ if (cod_info_w.block_type == SHORT_TYPE
+ && (distort[cod_info_w.sfbmax + 1] > 1.0
+ || distort[cod_info_w.sfbmax + 2] > 1.0))
+ break;
+ }
+
+ /* try a new scalefactor conbination on cod_info_w */
+ if (balance_noise(gfc, &cod_info_w, distort, xrpow, bRefine) == 0)
+ break;
+ if (cod_info_w.scalefac_scale)
+ maxggain = 254;
+
+ /* inner_loop starts with the initial quantization step computed above
+ * and slowly increases until the bits < huff_bits.
+ * Thus it is important not to start with too large of an inital
+ * quantization step. Too small is ok, but inner_loop will take longer
+ */
+ huff_bits = targ_bits - cod_info_w.part2_length;
+ if (huff_bits <= 0)
+ break;
+
+ /* increase quantizer stepsize until needed bits are below maximum
+ */
+ while ((cod_info_w.part2_3_length
+ = count_bits(gfc, xrpow, &cod_info_w, &prev_noise)) > huff_bits
+ && cod_info_w.global_gain <= maxggain)
+ cod_info_w.global_gain++;
+
+ if (cod_info_w.global_gain > maxggain)
+ break;
+
+ if (best_noise_info.over_count == 0) {
+
+ while ((cod_info_w.part2_3_length
+ = count_bits(gfc, xrpow, &cod_info_w, &prev_noise)) > best_part2_3_length
+ && cod_info_w.global_gain <= maxggain)
+ cod_info_w.global_gain++;
+
+ if (cod_info_w.global_gain > maxggain)
+ break;
+ }
+
+ /* compute the distortion in this quantization */
+ (void) calc_noise(&cod_info_w, l3_xmin, distort, &noise_info, &prev_noise);
+ noise_info.bits = cod_info_w.part2_3_length;
+
+ /* check if this quantization is better
+ * than our saved quantization */
+ if (cod_info->block_type != SHORT_TYPE) /* NORM, START or STOP type */
+ better = cfg->quant_comp;
+ else
+ better = cfg->quant_comp_short;
+
+
+ better = quant_compare(better, &best_noise_info, &noise_info, &cod_info_w, distort);
+
+
+ /* save data so we can restore this quantization later */
+ if (better) {
+ best_part2_3_length = cod_info->part2_3_length;
+ best_noise_info = noise_info;
+ *cod_info = cod_info_w;
+ age = 0;
+ /* save data so we can restore this quantization later */
+ /*if (cfg->vbr == vbr_rh || cfg->vbr == vbr_mtrh) */ {
+ /* store for later reuse */
+ memcpy(save_xrpow, xrpow, sizeof(FLOAT) * 576);
+ }
+ }
+ else {
+ /* early stop? */
+ if (cfg->full_outer_loop == 0) {
+ if (++age > search_limit && best_noise_info.over_count == 0)
+ break;
+ if ((cfg->noise_shaping_amp == 3) && bRefine && age > 30)
+ break;
+ if ((cfg->noise_shaping_amp == 3) && bRefine &&
+ (cod_info_w.global_gain - best_ggain_pass1) > 15)
+ break;
+ }
+ }
+ }
+ while ((cod_info_w.global_gain + cod_info_w.scalefac_scale) < 255);
+
+ if (cfg->noise_shaping_amp == 3) {
+ if (!bRefine) {
+ /* refine search */
+ cod_info_w = *cod_info;
+ memcpy(xrpow, save_xrpow, sizeof(FLOAT) * 576);
+ age = 0;
+ best_ggain_pass1 = cod_info_w.global_gain;
+
+ bRefine = 1;
+ }
+ else {
+ /* search already refined, stop */
+ bEndOfSearch = 1;
+ }
+
+ }
+ else {
+ bEndOfSearch = 1;
+ }
+ }
+
+ assert((cod_info->global_gain + cod_info->scalefac_scale) <= 255);
+ /* finish up
+ */
+ if (cfg->vbr == vbr_rh || cfg->vbr == vbr_mtrh || cfg->vbr == vbr_mt)
+ /* restore for reuse on next try */
+ memcpy(xrpow, save_xrpow, sizeof(FLOAT) * 576);
+ /* do the 'substep shaping'
+ */
+ else if (gfc->sv_qnt.substep_shaping & 1)
+ trancate_smallspectrums(gfc, cod_info, l3_xmin, xrpow);
+
+ return best_noise_info.over_count;
+}
+
+
+
+
+
+/************************************************************************
+ *
+ * iteration_finish_one()
+ *
+ * Robert Hegemann 2000-09-06
+ *
+ * update reservoir status after FINAL quantization/bitrate
+ *
+ ************************************************************************/
+
+static void
+iteration_finish_one(lame_internal_flags * gfc, int gr, int ch)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+ gr_info *const cod_info = &l3_side->tt[gr][ch];
+
+ /* try some better scalefac storage
+ */
+ best_scalefac_store(gfc, gr, ch, l3_side);
+
+ /* best huffman_divide may save some bits too
+ */
+ if (cfg->use_best_huffman == 1)
+ best_huffman_divide(gfc, cod_info);
+
+ /* update reservoir status after FINAL quantization/bitrate
+ */
+ ResvAdjust(gfc, cod_info);
+}
+
+
+
+/*********************************************************************
+ *
+ * VBR_encode_granule()
+ *
+ * 2000-09-04 Robert Hegemann
+ *
+ *********************************************************************/
+
+static void
+VBR_encode_granule(lame_internal_flags * gfc, gr_info * const cod_info, const FLOAT * const l3_xmin, /* allowed distortion of the scalefactor */
+ FLOAT xrpow[576], /* coloured magnitudes of spectral values */
+ const int ch, int min_bits, int max_bits)
+{
+ gr_info bst_cod_info;
+ FLOAT bst_xrpow[576];
+ int const Max_bits = max_bits;
+ int real_bits = max_bits + 1;
+ int this_bits = (max_bits + min_bits) / 2;
+ int dbits, over, found = 0;
+ int const sfb21_extra = gfc->sv_qnt.sfb21_extra;
+
+ assert(Max_bits <= MAX_BITS_PER_CHANNEL);
+ memset(bst_cod_info.l3_enc, 0, sizeof(bst_cod_info.l3_enc));
+
+ /* search within round about 40 bits of optimal
+ */
+ do {
+ assert(this_bits >= min_bits);
+ assert(this_bits <= max_bits);
+ assert(min_bits <= max_bits);
+
+ if (this_bits > Max_bits - 42)
+ gfc->sv_qnt.sfb21_extra = 0;
+ else
+ gfc->sv_qnt.sfb21_extra = sfb21_extra;
+
+ over = outer_loop(gfc, cod_info, l3_xmin, xrpow, ch, this_bits);
+
+ /* is quantization as good as we are looking for ?
+ * in this case: is no scalefactor band distorted?
+ */
+ if (over <= 0) {
+ found = 1;
+ /* now we know it can be done with "real_bits"
+ * and maybe we can skip some iterations
+ */
+ real_bits = cod_info->part2_3_length;
+
+ /* store best quantization so far
+ */
+ bst_cod_info = *cod_info;
+ memcpy(bst_xrpow, xrpow, sizeof(FLOAT) * 576);
+
+ /* try with fewer bits
+ */
+ max_bits = real_bits - 32;
+ dbits = max_bits - min_bits;
+ this_bits = (max_bits + min_bits) / 2;
+ }
+ else {
+ /* try with more bits
+ */
+ min_bits = this_bits + 32;
+ dbits = max_bits - min_bits;
+ this_bits = (max_bits + min_bits) / 2;
+
+ if (found) {
+ found = 2;
+ /* start again with best quantization so far
+ */
+ *cod_info = bst_cod_info;
+ memcpy(xrpow, bst_xrpow, sizeof(FLOAT) * 576);
+ }
+ }
+ } while (dbits > 12);
+
+ gfc->sv_qnt.sfb21_extra = sfb21_extra;
+
+ /* found=0 => nothing found, use last one
+ * found=1 => we just found the best and left the loop
+ * found=2 => we restored a good one and have now l3_enc to restore too
+ */
+ if (found == 2) {
+ memcpy(cod_info->l3_enc, bst_cod_info.l3_enc, sizeof(int) * 576);
+ }
+ assert(cod_info->part2_3_length <= Max_bits);
+
+}
+
+
+
+/************************************************************************
+ *
+ * get_framebits()
+ *
+ * Robert Hegemann 2000-09-05
+ *
+ * calculates
+ * * how many bits are available for analog silent granules
+ * * how many bits to use for the lowest allowed bitrate
+ * * how many bits each bitrate would provide
+ *
+ ************************************************************************/
+
+static void
+get_framebits(lame_internal_flags * gfc, int frameBits[15])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+ int bitsPerFrame, i;
+
+ /* always use at least this many bits per granule per channel
+ * unless we detect analog silence, see below
+ */
+ eov->bitrate_index = cfg->vbr_min_bitrate_index;
+ bitsPerFrame = getframebits(gfc);
+
+ /* bits for analog silence
+ */
+ eov->bitrate_index = 1;
+ bitsPerFrame = getframebits(gfc);
+
+ for (i = 1; i <= cfg->vbr_max_bitrate_index; i++) {
+ eov->bitrate_index = i;
+ frameBits[i] = ResvFrameBegin(gfc, &bitsPerFrame);
+ }
+}
+
+
+
+/*********************************************************************
+ *
+ * VBR_prepare()
+ *
+ * 2000-09-04 Robert Hegemann
+ *
+ * * converts LR to MS coding when necessary
+ * * calculates allowed/adjusted quantization noise amounts
+ * * detects analog silent frames
+ *
+ * some remarks:
+ * - lower masking depending on Quality setting
+ * - quality control together with adjusted ATH MDCT scaling
+ * on lower quality setting allocate more noise from
+ * ATH masking, and on higher quality setting allocate
+ * less noise from ATH masking.
+ * - experiments show that going more than 2dB over GPSYCHO's
+ * limits ends up in very annoying artefacts
+ *
+ *********************************************************************/
+
+/* RH: this one needs to be overhauled sometime */
+
+static int
+VBR_old_prepare(lame_internal_flags * gfc,
+ const FLOAT pe[2][2], FLOAT const ms_ener_ratio[2],
+ const III_psy_ratio ratio[2][2],
+ FLOAT l3_xmin[2][2][SFBMAX],
+ int frameBits[16], int min_bits[2][2], int max_bits[2][2], int bands[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+
+ FLOAT masking_lower_db, adjust = 0.0;
+ int gr, ch;
+ int analog_silence = 1;
+ int avg, mxb, bits = 0;
+
+ eov->bitrate_index = cfg->vbr_max_bitrate_index;
+ avg = ResvFrameBegin(gfc, &avg) / cfg->mode_gr;
+
+ get_framebits(gfc, frameBits);
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ mxb = on_pe(gfc, pe, max_bits[gr], avg, gr, 0);
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ ms_convert(&gfc->l3_side, gr);
+ reduce_side(max_bits[gr], ms_ener_ratio[gr], avg, mxb);
+ }
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ gr_info *const cod_info = &gfc->l3_side.tt[gr][ch];
+
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type */
+ adjust = 1.28 / (1 + exp(3.5 - pe[gr][ch] / 300.)) - 0.05;
+ masking_lower_db = gfc->sv_qnt.mask_adjust - adjust;
+ }
+ else {
+ adjust = 2.56 / (1 + exp(3.5 - pe[gr][ch] / 300.)) - 0.14;
+ masking_lower_db = gfc->sv_qnt.mask_adjust_short - adjust;
+ }
+ gfc->sv_qnt.masking_lower = pow(10.0, masking_lower_db * 0.1);
+
+ init_outer_loop(gfc, cod_info);
+ bands[gr][ch] = calc_xmin(gfc, &ratio[gr][ch], cod_info, l3_xmin[gr][ch]);
+ if (bands[gr][ch])
+ analog_silence = 0;
+
+ min_bits[gr][ch] = 126;
+
+ bits += max_bits[gr][ch];
+ }
+ }
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ if (bits > frameBits[cfg->vbr_max_bitrate_index] && bits > 0) {
+ max_bits[gr][ch] *= frameBits[cfg->vbr_max_bitrate_index];
+ max_bits[gr][ch] /= bits;
+ }
+ if (min_bits[gr][ch] > max_bits[gr][ch])
+ min_bits[gr][ch] = max_bits[gr][ch];
+
+ } /* for ch */
+ } /* for gr */
+
+ return analog_silence;
+}
+
+static void
+bitpressure_strategy(lame_internal_flags const *gfc,
+ FLOAT l3_xmin[2][2][SFBMAX], const int min_bits[2][2], int max_bits[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int gr, ch, sfb;
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info const *const gi = &gfc->l3_side.tt[gr][ch];
+ FLOAT *pxmin = l3_xmin[gr][ch];
+ for (sfb = 0; sfb < gi->psy_lmax; sfb++)
+ *pxmin++ *= 1. + .029 * sfb * sfb / SBMAX_l / SBMAX_l;
+
+ if (gi->block_type == SHORT_TYPE) {
+ for (sfb = gi->sfb_smin; sfb < SBMAX_s; sfb++) {
+ *pxmin++ *= 1. + .029 * sfb * sfb / SBMAX_s / SBMAX_s;
+ *pxmin++ *= 1. + .029 * sfb * sfb / SBMAX_s / SBMAX_s;
+ *pxmin++ *= 1. + .029 * sfb * sfb / SBMAX_s / SBMAX_s;
+ }
+ }
+ max_bits[gr][ch] = Max(min_bits[gr][ch], 0.9 * max_bits[gr][ch]);
+ }
+ }
+}
+
+/************************************************************************
+ *
+ * VBR_iteration_loop()
+ *
+ * tries to find out how many bits are needed for each granule and channel
+ * to get an acceptable quantization. An appropriate bitrate will then be
+ * choosed for quantization. rh 8/99
+ *
+ * Robert Hegemann 2000-09-06 rewrite
+ *
+ ************************************************************************/
+
+void
+VBR_old_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ener_ratio[2], const III_psy_ratio ratio[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+ FLOAT l3_xmin[2][2][SFBMAX];
+
+ FLOAT xrpow[576];
+ int bands[2][2];
+ int frameBits[15];
+ int used_bits;
+ int bits;
+ int min_bits[2][2], max_bits[2][2];
+ int mean_bits;
+ int ch, gr, analog_silence;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+
+ analog_silence = VBR_old_prepare(gfc, pe, ms_ener_ratio, ratio,
+ l3_xmin, frameBits, min_bits, max_bits, bands);
+
+ /*---------------------------------*/
+ for (;;) {
+
+ /* quantize granules with lowest possible number of bits
+ */
+
+ used_bits = 0;
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ int ret;
+ gr_info *const cod_info = &l3_side->tt[gr][ch];
+
+ /* init_outer_loop sets up cod_info, scalefac and xrpow
+ */
+ ret = init_xrpow(gfc, cod_info, xrpow);
+ if (ret == 0 || max_bits[gr][ch] == 0) {
+ /* xr contains no energy
+ * l3_enc, our encoding data, will be quantized to zero
+ */
+ continue; /* with next channel */
+ }
+
+ VBR_encode_granule(gfc, cod_info, l3_xmin[gr][ch], xrpow,
+ ch, min_bits[gr][ch], max_bits[gr][ch]);
+
+ /* do the 'substep shaping'
+ */
+ if (gfc->sv_qnt.substep_shaping & 1) {
+ trancate_smallspectrums(gfc, &l3_side->tt[gr][ch], l3_xmin[gr][ch], xrpow);
+ }
+
+ ret = cod_info->part2_3_length + cod_info->part2_length;
+ used_bits += ret;
+ } /* for ch */
+ } /* for gr */
+
+ /* find lowest bitrate able to hold used bits
+ */
+ if (analog_silence && !cfg->enforce_min_bitrate)
+ /* we detected analog silence and the user did not specify
+ * any hard framesize limit, so start with smallest possible frame
+ */
+ eov->bitrate_index = 1;
+ else
+ eov->bitrate_index = cfg->vbr_min_bitrate_index;
+
+ for (; eov->bitrate_index < cfg->vbr_max_bitrate_index; eov->bitrate_index++) {
+ if (used_bits <= frameBits[eov->bitrate_index])
+ break;
+ }
+ bits = ResvFrameBegin(gfc, &mean_bits);
+
+ if (used_bits <= bits)
+ break;
+
+ bitpressure_strategy(gfc, l3_xmin, (const int (*)[2])min_bits, max_bits);
+
+ } /* breaks adjusted */
+ /*--------------------------------------*/
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ iteration_finish_one(gfc, gr, ch);
+ } /* for ch */
+ } /* for gr */
+ ResvFrameEnd(gfc, mean_bits);
+}
+
+
+
+static int
+VBR_new_prepare(lame_internal_flags * gfc,
+ const FLOAT pe[2][2], const III_psy_ratio ratio[2][2],
+ FLOAT l3_xmin[2][2][SFBMAX], int frameBits[16], int max_bits[2][2],
+ int* max_resv)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+
+ int gr, ch;
+ int analog_silence = 1;
+ int avg, bits = 0;
+ int maximum_framebits;
+
+ if (!cfg->free_format) {
+ eov->bitrate_index = cfg->vbr_max_bitrate_index;
+ (void) ResvFrameBegin(gfc, &avg);
+ *max_resv = gfc->sv_enc.ResvMax;
+
+ get_framebits(gfc, frameBits);
+ maximum_framebits = frameBits[cfg->vbr_max_bitrate_index];
+ }
+ else {
+ eov->bitrate_index = 0;
+ maximum_framebits = ResvFrameBegin(gfc, &avg);
+ frameBits[0] = maximum_framebits;
+ *max_resv = gfc->sv_enc.ResvMax;
+ }
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ (void) on_pe(gfc, pe, max_bits[gr], avg, gr, 0);
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ ms_convert(&gfc->l3_side, gr);
+ }
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ gr_info *const cod_info = &gfc->l3_side.tt[gr][ch];
+
+ gfc->sv_qnt.masking_lower = pow(10.0, gfc->sv_qnt.mask_adjust * 0.1);
+
+ init_outer_loop(gfc, cod_info);
+ if (0 != calc_xmin(gfc, &ratio[gr][ch], cod_info, l3_xmin[gr][ch]))
+ analog_silence = 0;
+
+ bits += max_bits[gr][ch];
+ }
+ }
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ if (bits > maximum_framebits && bits > 0) {
+ max_bits[gr][ch] *= maximum_framebits;
+ max_bits[gr][ch] /= bits;
+ }
+
+ } /* for ch */
+ } /* for gr */
+ if (analog_silence) {
+ *max_resv = 0;
+ }
+ return analog_silence;
+}
+
+
+
+void
+VBR_new_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ener_ratio[2], const III_psy_ratio ratio[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+ FLOAT l3_xmin[2][2][SFBMAX];
+
+ FLOAT xrpow[2][2][576];
+ int frameBits[15];
+ int used_bits;
+ int max_bits[2][2];
+ int ch, gr, analog_silence, pad;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+
+ const FLOAT (*const_l3_xmin)[2][SFBMAX] = (const FLOAT (*)[2][SFBMAX])l3_xmin;
+ const FLOAT (*const_xrpow)[2][576] = (const FLOAT (*)[2][576])xrpow;
+ const int (*const_max_bits)[2] = (const int (*)[2])max_bits;
+
+ (void) ms_ener_ratio; /* not used */
+
+ memset(xrpow, 0, sizeof(xrpow));
+
+ analog_silence = VBR_new_prepare(gfc, pe, ratio, l3_xmin, frameBits, max_bits, &pad);
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info *const cod_info = &l3_side->tt[gr][ch];
+
+ /* init_outer_loop sets up cod_info, scalefac and xrpow
+ */
+ if (0 == init_xrpow(gfc, cod_info, xrpow[gr][ch])) {
+ max_bits[gr][ch] = 0; /* silent granule needs no bits */
+ }
+ } /* for ch */
+ } /* for gr */
+
+ /* quantize granules with lowest possible number of bits
+ */
+
+ used_bits = VBR_encode_frame(gfc, const_xrpow, const_l3_xmin, const_max_bits);
+
+ if (!cfg->free_format) {
+ int i, j;
+
+ /* find lowest bitrate able to hold used bits
+ */
+ if (analog_silence && !cfg->enforce_min_bitrate) {
+ /* we detected analog silence and the user did not specify
+ * any hard framesize limit, so start with smallest possible frame
+ */
+ i = 1;
+ }
+ else {
+ i = cfg->vbr_min_bitrate_index;
+ }
+
+ for (; i < cfg->vbr_max_bitrate_index; i++) {
+ if (used_bits <= frameBits[i])
+ break;
+ }
+ if (i > cfg->vbr_max_bitrate_index) {
+ i = cfg->vbr_max_bitrate_index;
+ }
+ if (pad > 0) {
+ for (j = cfg->vbr_max_bitrate_index; j > i; --j) {
+ int const unused = frameBits[j] - used_bits;
+ if (unused <= pad)
+ break;
+ }
+ eov->bitrate_index = j;
+ }
+ else {
+ eov->bitrate_index = i;
+ }
+ }
+ else {
+#if 0
+ static int mmm = 0;
+ int fff = getFramesize_kbps(gfc, used_bits);
+ int hhh = getFramesize_kbps(gfc, MAX_BITS_PER_GRANULE * cfg->mode_gr);
+ if (mmm < fff)
+ mmm = fff;
+ printf("demand=%3d kbps max=%3d kbps limit=%3d kbps\n", fff, mmm, hhh);
+#endif
+ eov->bitrate_index = 0;
+ }
+ if (used_bits <= frameBits[eov->bitrate_index]) {
+ /* update Reservoire status */
+ int mean_bits, fullframebits;
+ fullframebits = ResvFrameBegin(gfc, &mean_bits);
+ assert(used_bits <= fullframebits);
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info const *const cod_info = &l3_side->tt[gr][ch];
+ ResvAdjust(gfc, cod_info);
+ }
+ }
+ ResvFrameEnd(gfc, mean_bits);
+ }
+ else {
+ /* SHOULD NOT HAPPEN INTERNAL ERROR
+ */
+ ERRORF(gfc, "INTERNAL ERROR IN VBR NEW CODE, please send bug report\n");
+ exit(-1);
+ }
+}
+
+
+
+
+
+/********************************************************************
+ *
+ * calc_target_bits()
+ *
+ * calculates target bits for ABR encoding
+ *
+ * mt 2000/05/31
+ *
+ ********************************************************************/
+
+static void
+calc_target_bits(lame_internal_flags * gfc,
+ const FLOAT pe[2][2],
+ FLOAT const ms_ener_ratio[2],
+ int targ_bits[2][2], int *analog_silence_bits, int *max_frame_bits)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+ III_side_info_t const *const l3_side = &gfc->l3_side;
+ FLOAT res_factor;
+ int gr, ch, totbits, mean_bits;
+ int framesize = 576 * cfg->mode_gr;
+
+ eov->bitrate_index = cfg->vbr_max_bitrate_index;
+ *max_frame_bits = ResvFrameBegin(gfc, &mean_bits);
+
+ eov->bitrate_index = 1;
+ mean_bits = getframebits(gfc) - cfg->sideinfo_len * 8;
+ *analog_silence_bits = mean_bits / (cfg->mode_gr * cfg->channels_out);
+
+ mean_bits = cfg->vbr_avg_bitrate_kbps * framesize * 1000;
+ if (gfc->sv_qnt.substep_shaping & 1)
+ mean_bits *= 1.09;
+ mean_bits /= cfg->samplerate_out;
+ mean_bits -= cfg->sideinfo_len * 8;
+ mean_bits /= (cfg->mode_gr * cfg->channels_out);
+
+ /*
+ res_factor is the percentage of the target bitrate that should
+ be used on average. the remaining bits are added to the
+ bitreservoir and used for difficult to encode frames.
+
+ Since we are tracking the average bitrate, we should adjust
+ res_factor "on the fly", increasing it if the average bitrate
+ is greater than the requested bitrate, and decreasing it
+ otherwise. Reasonable ranges are from .9 to 1.0
+
+ Until we get the above suggestion working, we use the following
+ tuning:
+ compression ratio res_factor
+ 5.5 (256kbps) 1.0 no need for bitreservoir
+ 11 (128kbps) .93 7% held for reservoir
+
+ with linear interpolation for other values.
+
+ */
+ res_factor = .93 + .07 * (11.0 - cfg->compression_ratio) / (11.0 - 5.5);
+ if (res_factor < .90)
+ res_factor = .90;
+ if (res_factor > 1.00)
+ res_factor = 1.00;
+
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ int sum = 0;
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ targ_bits[gr][ch] = res_factor * mean_bits;
+
+ if (pe[gr][ch] > 700) {
+ int add_bits = (pe[gr][ch] - 700) / 1.4;
+
+ gr_info const *const cod_info = &l3_side->tt[gr][ch];
+ targ_bits[gr][ch] = res_factor * mean_bits;
+
+ /* short blocks use a little extra, no matter what the pe */
+ if (cod_info->block_type == SHORT_TYPE) {
+ if (add_bits < mean_bits / 2)
+ add_bits = mean_bits / 2;
+ }
+ /* at most increase bits by 1.5*average */
+ if (add_bits > mean_bits * 3 / 2)
+ add_bits = mean_bits * 3 / 2;
+ else if (add_bits < 0)
+ add_bits = 0;
+
+ targ_bits[gr][ch] += add_bits;
+ }
+ if (targ_bits[gr][ch] > MAX_BITS_PER_CHANNEL) {
+ targ_bits[gr][ch] = MAX_BITS_PER_CHANNEL;
+ }
+ sum += targ_bits[gr][ch];
+ } /* for ch */
+ if (sum > MAX_BITS_PER_GRANULE) {
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ targ_bits[gr][ch] *= MAX_BITS_PER_GRANULE;
+ targ_bits[gr][ch] /= sum;
+ }
+ }
+ } /* for gr */
+
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR)
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ reduce_side(targ_bits[gr], ms_ener_ratio[gr], mean_bits * cfg->channels_out,
+ MAX_BITS_PER_GRANULE);
+ }
+
+ /* sum target bits
+ */
+ totbits = 0;
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ if (targ_bits[gr][ch] > MAX_BITS_PER_CHANNEL)
+ targ_bits[gr][ch] = MAX_BITS_PER_CHANNEL;
+ totbits += targ_bits[gr][ch];
+ }
+ }
+
+ /* repartion target bits if needed
+ */
+ if (totbits > *max_frame_bits && totbits > 0) {
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ targ_bits[gr][ch] *= *max_frame_bits;
+ targ_bits[gr][ch] /= totbits;
+ }
+ }
+ }
+}
+
+
+
+
+
+
+/********************************************************************
+ *
+ * ABR_iteration_loop()
+ *
+ * encode a frame with a disired average bitrate
+ *
+ * mt 2000/05/31
+ *
+ ********************************************************************/
+
+void
+ABR_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ener_ratio[2], const III_psy_ratio ratio[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncResult_t *const eov = &gfc->ov_enc;
+ FLOAT l3_xmin[SFBMAX];
+ FLOAT xrpow[576];
+ int targ_bits[2][2];
+ int mean_bits, max_frame_bits;
+ int ch, gr, ath_over;
+ int analog_silence_bits;
+ gr_info *cod_info;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+
+ mean_bits = 0;
+
+ calc_target_bits(gfc, pe, ms_ener_ratio, targ_bits, &analog_silence_bits, &max_frame_bits);
+
+ /* encode granules
+ */
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ ms_convert(&gfc->l3_side, gr);
+ }
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ FLOAT adjust, masking_lower_db;
+ cod_info = &l3_side->tt[gr][ch];
+
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type */
+ /* adjust = 1.28/(1+exp(3.5-pe[gr][ch]/300.))-0.05; */
+ adjust = 0;
+ masking_lower_db = gfc->sv_qnt.mask_adjust - adjust;
+ }
+ else {
+ /* adjust = 2.56/(1+exp(3.5-pe[gr][ch]/300.))-0.14; */
+ adjust = 0;
+ masking_lower_db = gfc->sv_qnt.mask_adjust_short - adjust;
+ }
+ gfc->sv_qnt.masking_lower = pow(10.0, masking_lower_db * 0.1);
+
+
+ /* cod_info, scalefac and xrpow get initialized in init_outer_loop
+ */
+ init_outer_loop(gfc, cod_info);
+ if (init_xrpow(gfc, cod_info, xrpow)) {
+ /* xr contains energy we will have to encode
+ * calculate the masking abilities
+ * find some good quantization in outer_loop
+ */
+ ath_over = calc_xmin(gfc, &ratio[gr][ch], cod_info, l3_xmin);
+ if (0 == ath_over) /* analog silence */
+ targ_bits[gr][ch] = analog_silence_bits;
+
+ (void) outer_loop(gfc, cod_info, l3_xmin, xrpow, ch, targ_bits[gr][ch]);
+ }
+ iteration_finish_one(gfc, gr, ch);
+ } /* ch */
+ } /* gr */
+
+ /* find a bitrate which can refill the resevoir to positive size.
+ */
+ for (eov->bitrate_index = cfg->vbr_min_bitrate_index;
+ eov->bitrate_index <= cfg->vbr_max_bitrate_index; eov->bitrate_index++) {
+ if (ResvFrameBegin(gfc, &mean_bits) >= 0)
+ break;
+ }
+ assert(eov->bitrate_index <= cfg->vbr_max_bitrate_index);
+
+ ResvFrameEnd(gfc, mean_bits);
+}
+
+
+
+
+
+
+/************************************************************************
+ *
+ * CBR_iteration_loop()
+ *
+ * author/date??
+ *
+ * encodes one frame of MP3 data with constant bitrate
+ *
+ ************************************************************************/
+
+void
+CBR_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ener_ratio[2], const III_psy_ratio ratio[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ FLOAT l3_xmin[SFBMAX];
+ FLOAT xrpow[576];
+ int targ_bits[2];
+ int mean_bits, max_bits;
+ int gr, ch;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+ gr_info *cod_info;
+
+ (void) ResvFrameBegin(gfc, &mean_bits);
+
+ /* quantize! */
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+
+ /* calculate needed bits
+ */
+ max_bits = on_pe(gfc, pe, targ_bits, mean_bits, gr, gr);
+
+ if (gfc->ov_enc.mode_ext == MPG_MD_MS_LR) {
+ ms_convert(&gfc->l3_side, gr);
+ reduce_side(targ_bits, ms_ener_ratio[gr], mean_bits, max_bits);
+ }
+
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ FLOAT adjust, masking_lower_db;
+ cod_info = &l3_side->tt[gr][ch];
+
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type */
+ /* adjust = 1.28/(1+exp(3.5-pe[gr][ch]/300.))-0.05; */
+ adjust = 0;
+ masking_lower_db = gfc->sv_qnt.mask_adjust - adjust;
+ }
+ else {
+ /* adjust = 2.56/(1+exp(3.5-pe[gr][ch]/300.))-0.14; */
+ adjust = 0;
+ masking_lower_db = gfc->sv_qnt.mask_adjust_short - adjust;
+ }
+ gfc->sv_qnt.masking_lower = pow(10.0, masking_lower_db * 0.1);
+
+ /* init_outer_loop sets up cod_info, scalefac and xrpow
+ */
+ init_outer_loop(gfc, cod_info);
+ if (init_xrpow(gfc, cod_info, xrpow)) {
+ /* xr contains energy we will have to encode
+ * calculate the masking abilities
+ * find some good quantization in outer_loop
+ */
+ (void) calc_xmin(gfc, &ratio[gr][ch], cod_info, l3_xmin);
+ (void) outer_loop(gfc, cod_info, l3_xmin, xrpow, ch, targ_bits[ch]);
+ }
+
+ iteration_finish_one(gfc, gr, ch);
+ assert(cod_info->part2_3_length <= MAX_BITS_PER_CHANNEL);
+ assert(cod_info->part2_3_length <= targ_bits[ch]);
+ } /* for ch */
+ } /* for gr */
+
+ ResvFrameEnd(gfc, mean_bits);
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/quantize.h b/libnative/src/main/cpp/module/mp3/lame/quantize.h
new file mode 100644
index 0000000000000000000000000000000000000000..56edcc70309b0f88eafac0f364dd8d9324a47f26
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/quantize.h
@@ -0,0 +1,38 @@
+/*
+ * MP3 quantization
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_QUANTIZE_H
+#define LAME_QUANTIZE_H
+
+void CBR_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ratio[2], const III_psy_ratio ratio[2][2]);
+
+void VBR_old_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ratio[2], const III_psy_ratio ratio[2][2]);
+
+void VBR_new_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ratio[2], const III_psy_ratio ratio[2][2]);
+
+void ABR_iteration_loop(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ratio[2], const III_psy_ratio ratio[2][2]);
+
+
+#endif /* LAME_QUANTIZE_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.c b/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.c
new file mode 100644
index 0000000000000000000000000000000000000000..26b4d2601d5d86d2f27abc8b31279011951698b2
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.c
@@ -0,0 +1,1073 @@
+/*
+ * quantize_pvt source file
+ *
+ * Copyright (c) 1999-2002 Takehiro Tominaga
+ * Copyright (c) 2000-2012 Robert Hegemann
+ * Copyright (c) 2001 Naoki Shibata
+ * Copyright (c) 2002-2005 Gabriel Bouvigne
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: quantize_pvt.c,v 1.169.2.2 2012/02/07 13:40:37 robert Exp $ */
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "quantize_pvt.h"
+#include "reservoir.h"
+#include "lame-analysis.h"
+#include
+
+
+#define NSATHSCALE 100 /* Assuming dynamic range=96dB, this value should be 92 */
+
+/*
+ The following table is used to implement the scalefactor
+ partitioning for MPEG2 as described in section
+ 2.4.3.2 of the IS. The indexing corresponds to the
+ way the tables are presented in the IS:
+
+ [table_number][row_in_table][column of nr_of_sfb]
+*/
+const int nr_of_sfb_block[6][3][4] = {
+ {
+ {6, 5, 5, 5},
+ {9, 9, 9, 9},
+ {6, 9, 9, 9}
+ },
+ {
+ {6, 5, 7, 3},
+ {9, 9, 12, 6},
+ {6, 9, 12, 6}
+ },
+ {
+ {11, 10, 0, 0},
+ {18, 18, 0, 0},
+ {15, 18, 0, 0}
+ },
+ {
+ {7, 7, 7, 0},
+ {12, 12, 12, 0},
+ {6, 15, 12, 0}
+ },
+ {
+ {6, 6, 6, 3},
+ {12, 9, 9, 6},
+ {6, 12, 9, 6}
+ },
+ {
+ {8, 8, 5, 0},
+ {15, 12, 9, 0},
+ {6, 18, 9, 0}
+ }
+};
+
+
+/* Table B.6: layer3 preemphasis */
+const int pretab[SBMAX_l] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0
+};
+
+/*
+ Here are MPEG1 Table B.8 and MPEG2 Table B.1
+ -- Layer III scalefactor bands.
+ Index into this using a method such as:
+ idx = fr_ps->header->sampling_frequency
+ + (fr_ps->header->version * 3)
+*/
+
+
+const scalefac_struct sfBandIndex[9] = {
+ { /* Table B.2.b: 22.05 kHz */
+ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464,
+ 522, 576},
+ {0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* Table B.2.c: 24 kHz */ /* docs: 332. mpg123(broken): 330 */
+ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 332, 394, 464,
+ 540, 576},
+ {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* Table B.2.a: 16 kHz */
+ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464,
+ 522, 576},
+ {0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* Table B.8.b: 44.1 kHz */
+ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418,
+ 576},
+ {0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* Table B.8.c: 48 kHz */
+ {0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384,
+ 576},
+ {0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* Table B.8.a: 32 kHz */
+ {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550,
+ 576},
+ {0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* MPEG-2.5 11.025 kHz */
+ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464,
+ 522, 576},
+ {0 / 3, 12 / 3, 24 / 3, 36 / 3, 54 / 3, 78 / 3, 108 / 3, 144 / 3, 186 / 3, 240 / 3, 312 / 3,
+ 402 / 3, 522 / 3, 576 / 3}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* MPEG-2.5 12 kHz */
+ {0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464,
+ 522, 576},
+ {0 / 3, 12 / 3, 24 / 3, 36 / 3, 54 / 3, 78 / 3, 108 / 3, 144 / 3, 186 / 3, 240 / 3, 312 / 3,
+ 402 / 3, 522 / 3, 576 / 3}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ },
+ { /* MPEG-2.5 8 kHz */
+ {0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570,
+ 572, 574, 576},
+ {0 / 3, 24 / 3, 48 / 3, 72 / 3, 108 / 3, 156 / 3, 216 / 3, 288 / 3, 372 / 3, 480 / 3, 486 / 3,
+ 492 / 3, 498 / 3, 576 / 3}
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb21 pseudo sub bands */
+ , {0, 0, 0, 0, 0, 0, 0} /* sfb12 pseudo sub bands */
+ }
+};
+
+
+
+FLOAT pow20[Q_MAX + Q_MAX2 + 1];
+FLOAT ipow20[Q_MAX];
+FLOAT pow43[PRECALC_SIZE];
+/* initialized in first call to iteration_init */
+#ifdef TAKEHIRO_IEEE754_HACK
+FLOAT adj43asm[PRECALC_SIZE];
+#else
+FLOAT adj43[PRECALC_SIZE];
+#endif
+
+/*
+compute the ATH for each scalefactor band
+cd range: 0..96db
+
+Input: 3.3kHz signal 32767 amplitude (3.3kHz is where ATH is smallest = -5db)
+longblocks: sfb=12 en0/bw=-11db max_en0 = 1.3db
+shortblocks: sfb=5 -9db 0db
+
+Input: 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 (repeated)
+longblocks: amp=1 sfb=12 en0/bw=-103 db max_en0 = -92db
+ amp=32767 sfb=12 -12 db -1.4db
+
+Input: 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 (repeated)
+shortblocks: amp=1 sfb=5 en0/bw= -99 -86
+ amp=32767 sfb=5 -9 db 4db
+
+
+MAX energy of largest wave at 3.3kHz = 1db
+AVE energy of largest wave at 3.3kHz = -11db
+Let's take AVE: -11db = maximum signal in sfb=12.
+Dynamic range of CD: 96db. Therefor energy of smallest audible wave
+in sfb=12 = -11 - 96 = -107db = ATH at 3.3kHz.
+
+ATH formula for this wave: -5db. To adjust to LAME scaling, we need
+ATH = ATH_formula - 103 (db)
+ATH = ATH * 2.5e-10 (ener)
+
+*/
+
+static FLOAT
+ATHmdct(SessionConfig_t const *cfg, FLOAT f)
+{
+ FLOAT ath;
+
+ ath = ATHformula(cfg, f);
+
+ if (cfg->ATHfixpoint > 0) {
+ ath -= cfg->ATHfixpoint;
+ }
+ else {
+ ath -= NSATHSCALE;
+ }
+ ath += cfg->ATH_offset_db;
+
+ /* modify the MDCT scaling for the ATH and convert to energy */
+ ath = powf(10.0f, ath * 0.1f);
+ return ath;
+}
+
+static void
+compute_ath(lame_internal_flags const* gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ FLOAT *const ATH_l = gfc->ATH->l;
+ FLOAT *const ATH_psfb21 = gfc->ATH->psfb21;
+ FLOAT *const ATH_s = gfc->ATH->s;
+ FLOAT *const ATH_psfb12 = gfc->ATH->psfb12;
+ int sfb, i, start, end;
+ FLOAT ATH_f;
+ FLOAT const samp_freq = cfg->samplerate_out;
+
+ for (sfb = 0; sfb < SBMAX_l; sfb++) {
+ start = gfc->scalefac_band.l[sfb];
+ end = gfc->scalefac_band.l[sfb + 1];
+ ATH_l[sfb] = FLOAT_MAX;
+ for (i = start; i < end; i++) {
+ FLOAT const freq = i * samp_freq / (2 * 576);
+ ATH_f = ATHmdct(cfg, freq); /* freq in kHz */
+ ATH_l[sfb] = Min(ATH_l[sfb], ATH_f);
+ }
+ }
+
+ for (sfb = 0; sfb < PSFB21; sfb++) {
+ start = gfc->scalefac_band.psfb21[sfb];
+ end = gfc->scalefac_band.psfb21[sfb + 1];
+ ATH_psfb21[sfb] = FLOAT_MAX;
+ for (i = start; i < end; i++) {
+ FLOAT const freq = i * samp_freq / (2 * 576);
+ ATH_f = ATHmdct(cfg, freq); /* freq in kHz */
+ ATH_psfb21[sfb] = Min(ATH_psfb21[sfb], ATH_f);
+ }
+ }
+
+ for (sfb = 0; sfb < SBMAX_s; sfb++) {
+ start = gfc->scalefac_band.s[sfb];
+ end = gfc->scalefac_band.s[sfb + 1];
+ ATH_s[sfb] = FLOAT_MAX;
+ for (i = start; i < end; i++) {
+ FLOAT const freq = i * samp_freq / (2 * 192);
+ ATH_f = ATHmdct(cfg, freq); /* freq in kHz */
+ ATH_s[sfb] = Min(ATH_s[sfb], ATH_f);
+ }
+ ATH_s[sfb] *= (gfc->scalefac_band.s[sfb + 1] - gfc->scalefac_band.s[sfb]);
+ }
+
+ for (sfb = 0; sfb < PSFB12; sfb++) {
+ start = gfc->scalefac_band.psfb12[sfb];
+ end = gfc->scalefac_band.psfb12[sfb + 1];
+ ATH_psfb12[sfb] = FLOAT_MAX;
+ for (i = start; i < end; i++) {
+ FLOAT const freq = i * samp_freq / (2 * 192);
+ ATH_f = ATHmdct(cfg, freq); /* freq in kHz */
+ ATH_psfb12[sfb] = Min(ATH_psfb12[sfb], ATH_f);
+ }
+ /*not sure about the following */
+ ATH_psfb12[sfb] *= (gfc->scalefac_band.s[13] - gfc->scalefac_band.s[12]);
+ }
+
+
+ /* no-ATH mode:
+ * reduce ATH to -200 dB
+ */
+
+ if (cfg->noATH) {
+ for (sfb = 0; sfb < SBMAX_l; sfb++) {
+ ATH_l[sfb] = 1E-20;
+ }
+ for (sfb = 0; sfb < PSFB21; sfb++) {
+ ATH_psfb21[sfb] = 1E-20;
+ }
+ for (sfb = 0; sfb < SBMAX_s; sfb++) {
+ ATH_s[sfb] = 1E-20;
+ }
+ for (sfb = 0; sfb < PSFB12; sfb++) {
+ ATH_psfb12[sfb] = 1E-20;
+ }
+ }
+
+ /* work in progress, don't rely on it too much
+ */
+ gfc->ATH->floor = 10. * log10(ATHmdct(cfg, -1.));
+
+ /*
+ { FLOAT g=10000, t=1e30, x;
+ for ( f = 100; f < 10000; f++ ) {
+ x = ATHmdct( cfg, f );
+ if ( t > x ) t = x, g = f;
+ }
+ printf("min=%g\n", g);
+ } */
+}
+
+
+static float const payload_long[2][4] =
+{ {-0.000f, -0.000f, -0.000f, +0.000f}
+, {-0.500f, -0.250f, -0.025f, +0.500f}
+};
+static float const payload_short[2][4] =
+{ {-0.000f, -0.000f, -0.000f, +0.000f}
+, {-2.000f, -1.000f, -0.050f, +0.500f}
+};
+
+/************************************************************************/
+/* initialization for iteration_loop */
+/************************************************************************/
+void
+iteration_init(lame_internal_flags * gfc)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+ FLOAT adjust, db;
+ int i, sel;
+
+ if (gfc->iteration_init_init == 0) {
+ gfc->iteration_init_init = 1;
+
+ l3_side->main_data_begin = 0;
+ compute_ath(gfc);
+
+ pow43[0] = 0.0;
+ for (i = 1; i < PRECALC_SIZE; i++)
+ pow43[i] = pow((FLOAT) i, 4.0 / 3.0);
+
+#ifdef TAKEHIRO_IEEE754_HACK
+ adj43asm[0] = 0.0;
+ for (i = 1; i < PRECALC_SIZE; i++)
+ adj43asm[i] = i - 0.5 - pow(0.5 * (pow43[i - 1] + pow43[i]), 0.75);
+#else
+ for (i = 0; i < PRECALC_SIZE - 1; i++)
+ adj43[i] = (i + 1) - pow(0.5 * (pow43[i] + pow43[i + 1]), 0.75);
+ adj43[i] = 0.5;
+#endif
+ for (i = 0; i < Q_MAX; i++)
+ ipow20[i] = pow(2.0, (double) (i - 210) * -0.1875);
+ for (i = 0; i <= Q_MAX + Q_MAX2; i++)
+ pow20[i] = pow(2.0, (double) (i - 210 - Q_MAX2) * 0.25);
+
+ huffman_init(gfc);
+ init_xrpow_core_init(gfc);
+
+ sel = 1;/* RH: all modes like vbr-new (cfg->vbr == vbr_mt || cfg->vbr == vbr_mtrh) ? 1 : 0;*/
+
+ /* long */
+ db = cfg->adjust_bass_db + payload_long[sel][0];
+ adjust = powf(10.f, db * 0.1f);
+ for (i = 0; i <= 6; ++i) {
+ gfc->sv_qnt.longfact[i] = adjust;
+ }
+ db = cfg->adjust_alto_db + payload_long[sel][1];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i <= 13; ++i) {
+ gfc->sv_qnt.longfact[i] = adjust;
+ }
+ db = cfg->adjust_treble_db + payload_long[sel][2];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i <= 20; ++i) {
+ gfc->sv_qnt.longfact[i] = adjust;
+ }
+ db = cfg->adjust_sfb21_db + payload_long[sel][3];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i < SBMAX_l; ++i) {
+ gfc->sv_qnt.longfact[i] = adjust;
+ }
+
+ /* short */
+ db = cfg->adjust_bass_db + payload_short[sel][0];
+ adjust = powf(10.f, db * 0.1f);
+ for (i = 0; i <= 2; ++i) {
+ gfc->sv_qnt.shortfact[i] = adjust;
+ }
+ db = cfg->adjust_alto_db + payload_short[sel][1];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i <= 6; ++i) {
+ gfc->sv_qnt.shortfact[i] = adjust;
+ }
+ db = cfg->adjust_treble_db + payload_short[sel][2];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i <= 11; ++i) {
+ gfc->sv_qnt.shortfact[i] = adjust;
+ }
+ db = cfg->adjust_sfb21_db + payload_short[sel][3];
+ adjust = powf(10.f, db * 0.1f);
+ for (; i < SBMAX_s; ++i) {
+ gfc->sv_qnt.shortfact[i] = adjust;
+ }
+ }
+}
+
+
+
+
+
+/************************************************************************
+ * allocate bits among 2 channels based on PE
+ * mt 6/99
+ * bugfixes rh 8/01: often allocated more than the allowed 4095 bits
+ ************************************************************************/
+int
+on_pe(lame_internal_flags * gfc, const FLOAT pe[][2], int targ_bits[2], int mean_bits, int gr, int cbr)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int extra_bits = 0, tbits, bits;
+ int add_bits[2] = {0, 0};
+ int max_bits; /* maximum allowed bits for this granule */
+ int ch;
+
+ /* allocate targ_bits for granule */
+ ResvMaxBits(gfc, mean_bits, &tbits, &extra_bits, cbr);
+ max_bits = tbits + extra_bits;
+ if (max_bits > MAX_BITS_PER_GRANULE) /* hard limit per granule */
+ max_bits = MAX_BITS_PER_GRANULE;
+
+ for (bits = 0, ch = 0; ch < cfg->channels_out; ++ch) {
+ /******************************************************************
+ * allocate bits for each channel
+ ******************************************************************/
+ targ_bits[ch] = Min(MAX_BITS_PER_CHANNEL, tbits / cfg->channels_out);
+
+ add_bits[ch] = targ_bits[ch] * pe[gr][ch] / 700.0 - targ_bits[ch];
+
+ /* at most increase bits by 1.5*average */
+ if (add_bits[ch] > mean_bits * 3 / 4)
+ add_bits[ch] = mean_bits * 3 / 4;
+ if (add_bits[ch] < 0)
+ add_bits[ch] = 0;
+
+ if (add_bits[ch] + targ_bits[ch] > MAX_BITS_PER_CHANNEL)
+ add_bits[ch] = Max(0, MAX_BITS_PER_CHANNEL - targ_bits[ch]);
+
+ bits += add_bits[ch];
+ }
+ if (bits > extra_bits && bits > 0) {
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ add_bits[ch] = extra_bits * add_bits[ch] / bits;
+ }
+ }
+
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ targ_bits[ch] += add_bits[ch];
+ extra_bits -= add_bits[ch];
+ }
+
+ for (bits = 0, ch = 0; ch < cfg->channels_out; ++ch) {
+ bits += targ_bits[ch];
+ }
+ if (bits > MAX_BITS_PER_GRANULE) {
+ int sum = 0;
+ for (ch = 0; ch < cfg->channels_out; ++ch) {
+ targ_bits[ch] *= MAX_BITS_PER_GRANULE;
+ targ_bits[ch] /= bits;
+ sum += targ_bits[ch];
+ }
+ assert(sum <= MAX_BITS_PER_GRANULE);
+ }
+
+ return max_bits;
+}
+
+
+
+
+void
+reduce_side(int targ_bits[2], FLOAT ms_ener_ratio, int mean_bits, int max_bits)
+{
+ int move_bits;
+ FLOAT fac;
+
+ assert(max_bits <= MAX_BITS_PER_GRANULE);
+ assert(targ_bits[0] + targ_bits[1] <= MAX_BITS_PER_GRANULE);
+
+ /* ms_ener_ratio = 0: allocate 66/33 mid/side fac=.33
+ * ms_ener_ratio =.5: allocate 50/50 mid/side fac= 0 */
+ /* 75/25 split is fac=.5 */
+ /* float fac = .50*(.5-ms_ener_ratio[gr])/.5; */
+ fac = .33 * (.5 - ms_ener_ratio) / .5;
+ if (fac < 0)
+ fac = 0;
+ if (fac > .5)
+ fac = .5;
+
+ /* number of bits to move from side channel to mid channel */
+ /* move_bits = fac*targ_bits[1]; */
+ move_bits = fac * .5 * (targ_bits[0] + targ_bits[1]);
+
+ if (move_bits > MAX_BITS_PER_CHANNEL - targ_bits[0]) {
+ move_bits = MAX_BITS_PER_CHANNEL - targ_bits[0];
+ }
+ if (move_bits < 0)
+ move_bits = 0;
+
+ if (targ_bits[1] >= 125) {
+ /* dont reduce side channel below 125 bits */
+ if (targ_bits[1] - move_bits > 125) {
+
+ /* if mid channel already has 2x more than average, dont bother */
+ /* mean_bits = bits per granule (for both channels) */
+ if (targ_bits[0] < mean_bits)
+ targ_bits[0] += move_bits;
+ targ_bits[1] -= move_bits;
+ }
+ else {
+ targ_bits[0] += targ_bits[1] - 125;
+ targ_bits[1] = 125;
+ }
+ }
+
+ move_bits = targ_bits[0] + targ_bits[1];
+ if (move_bits > max_bits) {
+ targ_bits[0] = (max_bits * targ_bits[0]) / move_bits;
+ targ_bits[1] = (max_bits * targ_bits[1]) / move_bits;
+ }
+ assert(targ_bits[0] <= MAX_BITS_PER_CHANNEL);
+ assert(targ_bits[1] <= MAX_BITS_PER_CHANNEL);
+ assert(targ_bits[0] + targ_bits[1] <= MAX_BITS_PER_GRANULE);
+}
+
+
+/**
+ * Robert Hegemann 2001-04-27:
+ * this adjusts the ATH, keeping the original noise floor
+ * affects the higher frequencies more than the lower ones
+ */
+
+FLOAT
+athAdjust(FLOAT a, FLOAT x, FLOAT athFloor, float ATHfixpoint)
+{
+ /* work in progress
+ */
+ FLOAT const o = 90.30873362f;
+ FLOAT const p = (ATHfixpoint < 1.f) ? 94.82444863f : ATHfixpoint;
+ FLOAT u = FAST_LOG10_X(x, 10.0f);
+ FLOAT const v = a * a;
+ FLOAT w = 0.0f;
+ u -= athFloor; /* undo scaling */
+ if (v > 1E-20f)
+ w = 1.f + FAST_LOG10_X(v, 10.0f / o);
+ if (w < 0)
+ w = 0.f;
+ u *= w;
+ u += athFloor + o - p; /* redo scaling */
+
+ return powf(10.f, 0.1f * u);
+}
+
+
+
+/*************************************************************************/
+/* calc_xmin */
+/*************************************************************************/
+
+/*
+ Calculate the allowed distortion for each scalefactor band,
+ as determined by the psychoacoustic model.
+ xmin(sb) = ratio(sb) * en(sb) / bw(sb)
+
+ returns number of sfb's with energy > ATH
+*/
+
+int
+calc_xmin(lame_internal_flags const *gfc,
+ III_psy_ratio const *const ratio, gr_info * const cod_info, FLOAT * pxmin)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int sfb, gsfb, j = 0, ath_over = 0, k;
+ ATH_t const *const ATH = gfc->ATH;
+ const FLOAT *const xr = cod_info->xr;
+ int max_nonzero;
+
+ for (gsfb = 0; gsfb < cod_info->psy_lmax; gsfb++) {
+ FLOAT en0, xmin;
+ FLOAT rh1, rh2, rh3;
+ int width, l;
+
+ xmin = athAdjust(ATH->adjust_factor, ATH->l[gsfb], ATH->floor, cfg->ATHfixpoint);
+ xmin *= gfc->sv_qnt.longfact[gsfb];
+
+ width = cod_info->width[gsfb];
+ rh1 = xmin / width;
+#ifdef DBL_EPSILON
+ rh2 = DBL_EPSILON;
+#else
+ rh2 = 2.2204460492503131e-016;
+#endif
+ en0 = 0.0;
+ for (l = 0; l < width; ++l) {
+ FLOAT const xa = xr[j++];
+ FLOAT const x2 = xa * xa;
+ en0 += x2;
+ rh2 += (x2 < rh1) ? x2 : rh1;
+ }
+ if (en0 > xmin)
+ ath_over++;
+
+ if (en0 < xmin) {
+ rh3 = en0;
+ }
+ else if (rh2 < xmin) {
+ rh3 = xmin;
+ }
+ else {
+ rh3 = rh2;
+ }
+ xmin = rh3;
+ {
+ FLOAT const e = ratio->en.l[gsfb];
+ if (e > 1e-12f) {
+ FLOAT x;
+ x = en0 * ratio->thm.l[gsfb] / e;
+ x *= gfc->sv_qnt.longfact[gsfb];
+ if (xmin < x)
+ xmin = x;
+ }
+ }
+ xmin = Max(xmin, DBL_EPSILON);
+ cod_info->energy_above_cutoff[gsfb] = (en0 > xmin+1e-14f) ? 1 : 0;
+ *pxmin++ = xmin;
+ } /* end of long block loop */
+
+
+
+
+ /*use this function to determine the highest non-zero coeff */
+ max_nonzero = 0;
+ for (k = 575; k > 0; --k) {
+ if (fabs(xr[k]) > 1e-12f) {
+ max_nonzero = k;
+ break;
+ }
+ }
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type, but not SHORT */
+ max_nonzero |= 1; /* only odd numbers */
+ }
+ else {
+ max_nonzero /= 6; /* 3 short blocks */
+ max_nonzero *= 6;
+ max_nonzero += 5;
+ }
+
+ if (gfc->sv_qnt.sfb21_extra == 0 && cfg->samplerate_out < 44000) {
+ int const sfb_l = (cfg->samplerate_out <= 8000) ? 17 : 21;
+ int const sfb_s = (cfg->samplerate_out <= 8000) ? 9 : 12;
+ int limit = 575;
+ if (cod_info->block_type != SHORT_TYPE) { /* NORM, START or STOP type, but not SHORT */
+ limit = gfc->scalefac_band.l[sfb_l]-1;
+ }
+ else {
+ limit = 3*gfc->scalefac_band.s[sfb_s]-1;
+ }
+ if (max_nonzero > limit) {
+ max_nonzero = limit;
+ }
+ }
+ cod_info->max_nonzero_coeff = max_nonzero;
+
+
+
+ for (sfb = cod_info->sfb_smin; gsfb < cod_info->psymax; sfb++, gsfb += 3) {
+ int width, b, l;
+ FLOAT tmpATH;
+
+ tmpATH = athAdjust(ATH->adjust_factor, ATH->s[sfb], ATH->floor, cfg->ATHfixpoint);
+ tmpATH *= gfc->sv_qnt.shortfact[sfb];
+
+ width = cod_info->width[gsfb];
+ for (b = 0; b < 3; b++) {
+ FLOAT en0 = 0.0, xmin = tmpATH;
+ FLOAT rh1, rh2, rh3;
+
+ rh1 = tmpATH / width;
+#ifdef DBL_EPSILON
+ rh2 = DBL_EPSILON;
+#else
+ rh2 = 2.2204460492503131e-016;
+#endif
+ for (l = 0; l < width; ++l) {
+ FLOAT const xa = xr[j++];
+ FLOAT const x2 = xa * xa;
+ en0 += x2;
+ rh2 += (x2 < rh1) ? x2 : rh1;
+ }
+ if (en0 > tmpATH)
+ ath_over++;
+
+ if (en0 < tmpATH) {
+ rh3 = en0;
+ }
+ else if (rh2 < tmpATH) {
+ rh3 = tmpATH;
+ }
+ else {
+ rh3 = rh2;
+ }
+ xmin = rh3;
+ {
+ FLOAT const e = ratio->en.s[sfb][b];
+ if (e > 1e-12f) {
+ FLOAT x;
+ x = en0 * ratio->thm.s[sfb][b] / e;
+ x *= gfc->sv_qnt.shortfact[sfb];
+ if (xmin < x)
+ xmin = x;
+ }
+ }
+ xmin = Max(xmin, DBL_EPSILON);
+ cod_info->energy_above_cutoff[gsfb+b] = (en0 > xmin+1e-14f) ? 1 : 0;
+ *pxmin++ = xmin;
+ } /* b */
+ if (cfg->use_temporal_masking_effect) {
+ if (pxmin[-3] > pxmin[-3 + 1])
+ pxmin[-3 + 1] += (pxmin[-3] - pxmin[-3 + 1]) * gfc->cd_psy->decay;
+ if (pxmin[-3 + 1] > pxmin[-3 + 2])
+ pxmin[-3 + 2] += (pxmin[-3 + 1] - pxmin[-3 + 2]) * gfc->cd_psy->decay;
+ }
+ } /* end of short block sfb loop */
+
+ return ath_over;
+}
+
+
+static FLOAT
+calc_noise_core_c(const gr_info * const cod_info, int *startline, int l, FLOAT step)
+{
+ FLOAT noise = 0;
+ int j = *startline;
+ const int *const ix = cod_info->l3_enc;
+
+ if (j > cod_info->count1) {
+ while (l--) {
+ FLOAT temp;
+ temp = cod_info->xr[j];
+ j++;
+ noise += temp * temp;
+ temp = cod_info->xr[j];
+ j++;
+ noise += temp * temp;
+ }
+ }
+ else if (j > cod_info->big_values) {
+ FLOAT ix01[2];
+ ix01[0] = 0;
+ ix01[1] = step;
+ while (l--) {
+ FLOAT temp;
+ temp = fabs(cod_info->xr[j]) - ix01[ix[j]];
+ j++;
+ noise += temp * temp;
+ temp = fabs(cod_info->xr[j]) - ix01[ix[j]];
+ j++;
+ noise += temp * temp;
+ }
+ }
+ else {
+ while (l--) {
+ FLOAT temp;
+ temp = fabs(cod_info->xr[j]) - pow43[ix[j]] * step;
+ j++;
+ noise += temp * temp;
+ temp = fabs(cod_info->xr[j]) - pow43[ix[j]] * step;
+ j++;
+ noise += temp * temp;
+ }
+ }
+
+ *startline = j;
+ return noise;
+}
+
+
+/*************************************************************************/
+/* calc_noise */
+/*************************************************************************/
+
+/* -oo dB => -1.00 */
+/* - 6 dB => -0.97 */
+/* - 3 dB => -0.80 */
+/* - 2 dB => -0.64 */
+/* - 1 dB => -0.38 */
+/* 0 dB => 0.00 */
+/* + 1 dB => +0.49 */
+/* + 2 dB => +1.06 */
+/* + 3 dB => +1.68 */
+/* + 6 dB => +3.69 */
+/* +10 dB => +6.45 */
+
+int
+calc_noise(gr_info const *const cod_info,
+ FLOAT const *l3_xmin,
+ FLOAT * distort, calc_noise_result * const res, calc_noise_data * prev_noise)
+{
+ int sfb, l, over = 0;
+ FLOAT over_noise_db = 0;
+ FLOAT tot_noise_db = 0; /* 0 dB relative to masking */
+ FLOAT max_noise = -20.0; /* -200 dB relative to masking */
+ int j = 0;
+ const int *scalefac = cod_info->scalefac;
+
+ res->over_SSD = 0;
+
+
+ for (sfb = 0; sfb < cod_info->psymax; sfb++) {
+ int const s =
+ cod_info->global_gain - (((*scalefac++) + (cod_info->preflag ? pretab[sfb] : 0))
+ << (cod_info->scalefac_scale + 1))
+ - cod_info->subblock_gain[cod_info->window[sfb]] * 8;
+ FLOAT const r_l3_xmin = 1.f / *l3_xmin++;
+ FLOAT distort_ = 0.0f;
+ FLOAT noise = 0.0f;
+
+ if (prev_noise && (prev_noise->step[sfb] == s)) {
+
+ /* use previously computed values */
+ j += cod_info->width[sfb];
+ distort_ = r_l3_xmin * prev_noise->noise[sfb];
+
+ noise = prev_noise->noise_log[sfb];
+
+ }
+ else {
+ FLOAT const step = POW20(s);
+ l = cod_info->width[sfb] >> 1;
+
+ if ((j + cod_info->width[sfb]) > cod_info->max_nonzero_coeff) {
+ int usefullsize;
+ usefullsize = cod_info->max_nonzero_coeff - j + 1;
+
+ if (usefullsize > 0)
+ l = usefullsize >> 1;
+ else
+ l = 0;
+ }
+
+ noise = calc_noise_core_c(cod_info, &j, l, step);
+
+
+ if (prev_noise) {
+ /* save noise values */
+ prev_noise->step[sfb] = s;
+ prev_noise->noise[sfb] = noise;
+ }
+
+ distort_ = r_l3_xmin * noise;
+
+ /* multiplying here is adding in dB, but can overflow */
+ noise = FAST_LOG10(Max(distort_, 1E-20f));
+
+ if (prev_noise) {
+ /* save noise values */
+ prev_noise->noise_log[sfb] = noise;
+ }
+ }
+ *distort++ = distort_;
+
+ if (prev_noise) {
+ /* save noise values */
+ prev_noise->global_gain = cod_info->global_gain;;
+ }
+
+
+ /*tot_noise *= Max(noise, 1E-20); */
+ tot_noise_db += noise;
+
+ if (noise > 0.0) {
+ int tmp;
+
+ tmp = Max((int) (noise * 10 + .5), 1);
+ res->over_SSD += tmp * tmp;
+
+ over++;
+ /* multiplying here is adding in dB -but can overflow */
+ /*over_noise *= noise; */
+ over_noise_db += noise;
+ }
+ max_noise = Max(max_noise, noise);
+
+ }
+
+ res->over_count = over;
+ res->tot_noise = tot_noise_db;
+ res->over_noise = over_noise_db;
+ res->max_noise = max_noise;
+
+ return over;
+}
+
+
+
+
+
+
+
+
+/************************************************************************
+ *
+ * set_pinfo()
+ *
+ * updates plotting data
+ *
+ * Mark Taylor 2000-??-??
+ *
+ * Robert Hegemann: moved noise/distortion calc into it
+ *
+ ************************************************************************/
+
+static void
+set_pinfo(lame_internal_flags const *gfc,
+ gr_info * const cod_info, const III_psy_ratio * const ratio, const int gr, const int ch)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int sfb, sfb2;
+ int j, i, l, start, end, bw;
+ FLOAT en0, en1;
+ FLOAT const ifqstep = (cod_info->scalefac_scale == 0) ? .5 : 1.0;
+ int const *const scalefac = cod_info->scalefac;
+
+ FLOAT l3_xmin[SFBMAX], xfsf[SFBMAX];
+ calc_noise_result noise;
+
+ (void) calc_xmin(gfc, ratio, cod_info, l3_xmin);
+ (void) calc_noise(cod_info, l3_xmin, xfsf, &noise, 0);
+
+ j = 0;
+ sfb2 = cod_info->sfb_lmax;
+ if (cod_info->block_type != SHORT_TYPE && !cod_info->mixed_block_flag)
+ sfb2 = 22;
+ for (sfb = 0; sfb < sfb2; sfb++) {
+ start = gfc->scalefac_band.l[sfb];
+ end = gfc->scalefac_band.l[sfb + 1];
+ bw = end - start;
+ for (en0 = 0.0; j < end; j++)
+ en0 += cod_info->xr[j] * cod_info->xr[j];
+ en0 /= bw;
+ /* convert to MDCT units */
+ en1 = 1e15; /* scaling so it shows up on FFT plot */
+ gfc->pinfo->en[gr][ch][sfb] = en1 * en0;
+ gfc->pinfo->xfsf[gr][ch][sfb] = en1 * l3_xmin[sfb] * xfsf[sfb] / bw;
+
+ if (ratio->en.l[sfb] > 0 && !cfg->ATHonly)
+ en0 = en0 / ratio->en.l[sfb];
+ else
+ en0 = 0.0;
+
+ gfc->pinfo->thr[gr][ch][sfb] = en1 * Max(en0 * ratio->thm.l[sfb], gfc->ATH->l[sfb]);
+
+ /* there is no scalefactor bands >= SBPSY_l */
+ gfc->pinfo->LAMEsfb[gr][ch][sfb] = 0;
+ if (cod_info->preflag && sfb >= 11)
+ gfc->pinfo->LAMEsfb[gr][ch][sfb] = -ifqstep * pretab[sfb];
+
+ if (sfb < SBPSY_l) {
+ assert(scalefac[sfb] >= 0); /* scfsi should be decoded by caller side */
+ gfc->pinfo->LAMEsfb[gr][ch][sfb] -= ifqstep * scalefac[sfb];
+ }
+ } /* for sfb */
+
+ if (cod_info->block_type == SHORT_TYPE) {
+ sfb2 = sfb;
+ for (sfb = cod_info->sfb_smin; sfb < SBMAX_s; sfb++) {
+ start = gfc->scalefac_band.s[sfb];
+ end = gfc->scalefac_band.s[sfb + 1];
+ bw = end - start;
+ for (i = 0; i < 3; i++) {
+ for (en0 = 0.0, l = start; l < end; l++) {
+ en0 += cod_info->xr[j] * cod_info->xr[j];
+ j++;
+ }
+ en0 = Max(en0 / bw, 1e-20);
+ /* convert to MDCT units */
+ en1 = 1e15; /* scaling so it shows up on FFT plot */
+
+ gfc->pinfo->en_s[gr][ch][3 * sfb + i] = en1 * en0;
+ gfc->pinfo->xfsf_s[gr][ch][3 * sfb + i] = en1 * l3_xmin[sfb2] * xfsf[sfb2] / bw;
+ if (ratio->en.s[sfb][i] > 0)
+ en0 = en0 / ratio->en.s[sfb][i];
+ else
+ en0 = 0.0;
+ if (cfg->ATHonly || cfg->ATHshort)
+ en0 = 0;
+
+ gfc->pinfo->thr_s[gr][ch][3 * sfb + i] =
+ en1 * Max(en0 * ratio->thm.s[sfb][i], gfc->ATH->s[sfb]);
+
+ /* there is no scalefactor bands >= SBPSY_s */
+ gfc->pinfo->LAMEsfb_s[gr][ch][3 * sfb + i]
+ = -2.0 * cod_info->subblock_gain[i];
+ if (sfb < SBPSY_s) {
+ gfc->pinfo->LAMEsfb_s[gr][ch][3 * sfb + i] -= ifqstep * scalefac[sfb2];
+ }
+ sfb2++;
+ }
+ }
+ } /* block type short */
+ gfc->pinfo->LAMEqss[gr][ch] = cod_info->global_gain;
+ gfc->pinfo->LAMEmainbits[gr][ch] = cod_info->part2_3_length + cod_info->part2_length;
+ gfc->pinfo->LAMEsfbits[gr][ch] = cod_info->part2_length;
+
+ gfc->pinfo->over[gr][ch] = noise.over_count;
+ gfc->pinfo->max_noise[gr][ch] = noise.max_noise * 10.0;
+ gfc->pinfo->over_noise[gr][ch] = noise.over_noise * 10.0;
+ gfc->pinfo->tot_noise[gr][ch] = noise.tot_noise * 10.0;
+ gfc->pinfo->over_SSD[gr][ch] = noise.over_SSD;
+}
+
+
+/************************************************************************
+ *
+ * set_frame_pinfo()
+ *
+ * updates plotting data for a whole frame
+ *
+ * Robert Hegemann 2000-10-21
+ *
+ ************************************************************************/
+
+void
+set_frame_pinfo(lame_internal_flags * gfc, const III_psy_ratio ratio[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int ch;
+ int gr;
+
+ /* for every granule and channel patch l3_enc and set info
+ */
+ for (gr = 0; gr < cfg->mode_gr; gr++) {
+ for (ch = 0; ch < cfg->channels_out; ch++) {
+ gr_info *const cod_info = &gfc->l3_side.tt[gr][ch];
+ int scalefac_sav[SFBMAX];
+ memcpy(scalefac_sav, cod_info->scalefac, sizeof(scalefac_sav));
+
+ /* reconstruct the scalefactors in case SCFSI was used
+ */
+ if (gr == 1) {
+ int sfb;
+ for (sfb = 0; sfb < cod_info->sfb_lmax; sfb++) {
+ if (cod_info->scalefac[sfb] < 0) /* scfsi */
+ cod_info->scalefac[sfb] = gfc->l3_side.tt[0][ch].scalefac[sfb];
+ }
+ }
+
+ set_pinfo(gfc, cod_info, &ratio[gr][ch], gr, ch);
+ memcpy(cod_info->scalefac, scalefac_sav, sizeof(scalefac_sav));
+ } /* for ch */
+ } /* for gr */
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.h b/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8333e7e9a457c7a2782716a642f8aac037621d6
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/quantize_pvt.h
@@ -0,0 +1,128 @@
+/*
+ * quantize_pvt include file
+ *
+ * Copyright (c) 1999 Takehiro TOMINAGA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_QUANTIZE_PVT_H
+#define LAME_QUANTIZE_PVT_H
+
+#define IXMAX_VAL 8206 /* ix always <= 8191+15. see count_bits() */
+
+/* buggy Winamp decoder cannot handle values > 8191 */
+/* #define IXMAX_VAL 8191 */
+
+#define PRECALC_SIZE (IXMAX_VAL+2)
+
+
+extern const int nr_of_sfb_block[6][3][4];
+extern const int pretab[SBMAX_l];
+extern const int slen1_tab[16];
+extern const int slen2_tab[16];
+
+extern const scalefac_struct sfBandIndex[9];
+
+extern FLOAT pow43[PRECALC_SIZE];
+#ifdef TAKEHIRO_IEEE754_HACK
+extern FLOAT adj43asm[PRECALC_SIZE];
+#else
+extern FLOAT adj43[PRECALC_SIZE];
+#endif
+
+#define Q_MAX (256+1)
+#define Q_MAX2 116 /* minimum possible number of
+ -cod_info->global_gain
+ + ((scalefac[] + (cod_info->preflag ? pretab[sfb] : 0))
+ << (cod_info->scalefac_scale + 1))
+ + cod_info->subblock_gain[cod_info->window[sfb]] * 8;
+
+ for long block, 0+((15+3)<<2) = 18*4 = 72
+ for short block, 0+(15<<2)+7*8 = 15*4+56 = 116
+ */
+
+extern FLOAT pow20[Q_MAX + Q_MAX2 + 1];
+extern FLOAT ipow20[Q_MAX];
+
+typedef struct calc_noise_result_t {
+ FLOAT over_noise; /* sum of quantization noise > masking */
+ FLOAT tot_noise; /* sum of all quantization noise */
+ FLOAT max_noise; /* max quantization noise */
+ int over_count; /* number of quantization noise > masking */
+ int over_SSD; /* SSD-like cost of distorted bands */
+ int bits;
+} calc_noise_result;
+
+
+/**
+* allows re-use of previously
+* computed noise values
+*/
+typedef struct calc_noise_data_t {
+ int global_gain;
+ int sfb_count1;
+ int step[39];
+ FLOAT noise[39];
+ FLOAT noise_log[39];
+} calc_noise_data;
+
+
+int on_pe(lame_internal_flags * gfc, const FLOAT pe[2][2],
+ int targ_bits[2], int mean_bits, int gr, int cbr);
+
+void reduce_side(int targ_bits[2], FLOAT ms_ener_ratio, int mean_bits, int max_bits);
+
+
+void iteration_init(lame_internal_flags * gfc);
+
+
+int calc_xmin(lame_internal_flags const *gfc,
+ III_psy_ratio const *const ratio, gr_info * const cod_info, FLOAT * l3_xmin);
+
+int calc_noise(const gr_info * const cod_info,
+ const FLOAT * l3_xmin,
+ FLOAT * distort, calc_noise_result * const res, calc_noise_data * prev_noise);
+
+void set_frame_pinfo(lame_internal_flags * gfc, const III_psy_ratio ratio[2][2]);
+
+
+
+
+/* takehiro.c */
+
+int count_bits(lame_internal_flags const *const gfc, const FLOAT * const xr,
+ gr_info * const cod_info, calc_noise_data * prev_noise);
+int noquant_count_bits(lame_internal_flags const *const gfc,
+ gr_info * const cod_info, calc_noise_data * prev_noise);
+
+
+void best_huffman_divide(const lame_internal_flags * const gfc, gr_info * const cod_info);
+
+void best_scalefac_store(const lame_internal_flags * gfc, const int gr, const int ch,
+ III_side_info_t * const l3_side);
+
+int scale_bitcount(const lame_internal_flags * gfc, gr_info * cod_info);
+
+void huffman_init(lame_internal_flags * const gfc);
+
+void init_xrpow_core_init(lame_internal_flags * const gfc);
+
+FLOAT athAdjust(FLOAT a, FLOAT x, FLOAT athFloor, float ATHfixpoint);
+
+#define LARGE_BITS 100000
+
+#endif /* LAME_QUANTIZE_PVT_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/reservoir.c b/libnative/src/main/cpp/module/mp3/lame/reservoir.c
new file mode 100644
index 0000000000000000000000000000000000000000..4fdaa8d33bcf53b976b934fa4fde635e76911787
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/reservoir.c
@@ -0,0 +1,293 @@
+/*
+ * bit reservoir source file
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: reservoir.c,v 1.45 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "reservoir.h"
+
+#include "bitstream.h"
+#include "lame-analysis.h"
+#include "lame_global_flags.h"
+
+
+/*
+ ResvFrameBegin:
+ Called (repeatedly) at the beginning of a frame. Updates the maximum
+ size of the reservoir, and checks to make sure main_data_begin
+ was set properly by the formatter
+*/
+
+/*
+ * Background information:
+ *
+ * This is the original text from the ISO standard. Because of
+ * sooo many bugs and irritations correcting comments are added
+ * in brackets []. A '^W' means you should remove the last word.
+ *
+ * 1) The following rule can be used to calculate the maximum
+ * number of bits used for one granule [^W frame]:
+ * At the highest possible bitrate of Layer III (320 kbps
+ * per stereo signal [^W^W^W], 48 kHz) the frames must be of
+ * [^W^W^W are designed to have] constant length, i.e.
+ * one buffer [^W^W the frame] length is:
+ *
+ * 320 kbps * 1152/48 kHz = 7680 bit = 960 byte
+ *
+ * This value is used as the maximum buffer per channel [^W^W] at
+ * lower bitrates [than 320 kbps]. At 64 kbps mono or 128 kbps
+ * stereo the main granule length is 64 kbps * 576/48 kHz = 768 bit
+ * [per granule and channel] at 48 kHz sampling frequency.
+ * This means that there is a maximum deviation (short time buffer
+ * [= reservoir]) of 7680 - 2*2*768 = 4608 bits is allowed at 64 kbps.
+ * The actual deviation is equal to the number of bytes [with the
+ * meaning of octets] denoted by the main_data_end offset pointer.
+ * The actual maximum deviation is (2^9-1)*8 bit = 4088 bits
+ * [for MPEG-1 and (2^8-1)*8 bit for MPEG-2, both are hard limits].
+ * ... The xchange of buffer bits between the left and right channel
+ * is allowed without restrictions [exception: dual channel].
+ * Because of the [constructed] constraint on the buffer size
+ * main_data_end is always set to 0 in the case of bit_rate_index==14,
+ * i.e. data rate 320 kbps per stereo signal [^W^W^W]. In this case
+ * all data are allocated between adjacent header [^W sync] words
+ * [, i.e. there is no buffering at all].
+ */
+
+int
+ResvFrameBegin(lame_internal_flags * gfc, int *mean_bits)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int fullFrameBits;
+ int resvLimit;
+ int maxmp3buf;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+ int frameLength;
+ int meanBits;
+
+ frameLength = getframebits(gfc);
+ meanBits = (frameLength - cfg->sideinfo_len * 8) / cfg->mode_gr;
+
+/*
+ * Meaning of the variables:
+ * resvLimit: (0, 8, ..., 8*255 (MPEG-2), 8*511 (MPEG-1))
+ * Number of bits can be stored in previous frame(s) due to
+ * counter size constaints
+ * maxmp3buf: ( ??? ... 8*1951 (MPEG-1 and 2), 8*2047 (MPEG-2.5))
+ * Number of bits allowed to encode one frame (you can take 8*511 bit
+ * from the bit reservoir and at most 8*1440 bit from the current
+ * frame (320 kbps, 32 kHz), so 8*1951 bit is the largest possible
+ * value for MPEG-1 and -2)
+ *
+ * maximum allowed granule/channel size times 4 = 8*2047 bits.,
+ * so this is the absolute maximum supported by the format.
+ *
+ *
+ * fullFrameBits: maximum number of bits available for encoding
+ * the current frame.
+ *
+ * mean_bits: target number of bits per granule.
+ *
+ * frameLength:
+ *
+ * gfc->ResvMax: maximum allowed reservoir
+ *
+ * gfc->ResvSize: current reservoir size
+ *
+ * l3_side->resvDrain_pre:
+ * ancillary data to be added to previous frame:
+ * (only usefull in VBR modes if it is possible to have
+ * maxmp3buf < fullFrameBits)). Currently disabled,
+ * see #define NEW_DRAIN
+ * 2010-02-13: RH now enabled, it seems to be needed for CBR too,
+ * as there exists one example, where the FhG decoder
+ * can't decode a -b320 CBR file anymore.
+ *
+ * l3_side->resvDrain_post:
+ * ancillary data to be added to this frame:
+ *
+ */
+
+ /* main_data_begin has 9 bits in MPEG-1, 8 bits MPEG-2 */
+ resvLimit = (8 * 256) * cfg->mode_gr - 8;
+
+ /* maximum allowed frame size. dont use more than this number of
+ bits, even if the frame has the space for them: */
+ maxmp3buf = cfg->buffer_constraint;
+ esv->ResvMax = maxmp3buf - frameLength;
+ if (esv->ResvMax > resvLimit)
+ esv->ResvMax = resvLimit;
+ if (esv->ResvMax < 0 || cfg->disable_reservoir)
+ esv->ResvMax = 0;
+
+ fullFrameBits = meanBits * cfg->mode_gr + Min(esv->ResvSize, esv->ResvMax);
+
+ if (fullFrameBits > maxmp3buf)
+ fullFrameBits = maxmp3buf;
+
+ assert(0 == esv->ResvMax % 8);
+ assert(esv->ResvMax >= 0);
+
+ l3_side->resvDrain_pre = 0;
+
+ if (gfc->pinfo != NULL) {
+ gfc->pinfo->mean_bits = meanBits / 2; /* expected bits per channel per granule [is this also right for mono/stereo, MPEG-1/2 ?] */
+ gfc->pinfo->resvsize = esv->ResvSize;
+ }
+ *mean_bits = meanBits;
+ return fullFrameBits;
+}
+
+
+/*
+ ResvMaxBits
+ returns targ_bits: target number of bits to use for 1 granule
+ extra_bits: amount extra available from reservoir
+ Mark Taylor 4/99
+*/
+void
+ResvMaxBits(lame_internal_flags * gfc, int mean_bits, int *targ_bits, int *extra_bits, int cbr)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ int add_bits, targBits, extraBits;
+ int ResvSize = esv->ResvSize, ResvMax = esv->ResvMax;
+
+ /* conpensate the saved bits used in the 1st granule */
+ if (cbr)
+ ResvSize += mean_bits;
+
+ if (gfc->sv_qnt.substep_shaping & 1)
+ ResvMax *= 0.9;
+
+ targBits = mean_bits;
+
+ /* extra bits if the reservoir is almost full */
+ if (ResvSize * 10 > ResvMax * 9) {
+ add_bits = ResvSize - (ResvMax * 9) / 10;
+ targBits += add_bits;
+ gfc->sv_qnt.substep_shaping |= 0x80;
+ }
+ else {
+ add_bits = 0;
+ gfc->sv_qnt.substep_shaping &= 0x7f;
+ /* build up reservoir. this builds the reservoir a little slower
+ * than FhG. It could simple be mean_bits/15, but this was rigged
+ * to always produce 100 (the old value) at 128kbs */
+ /* *targ_bits -= (int) (mean_bits/15.2); */
+ if (!cfg->disable_reservoir && !(gfc->sv_qnt.substep_shaping & 1))
+ targBits -= .1 * mean_bits;
+ }
+
+
+ /* amount from the reservoir we are allowed to use. ISO says 6/10 */
+ extraBits = (ResvSize < (esv->ResvMax * 6) / 10 ? ResvSize : (esv->ResvMax * 6) / 10);
+ extraBits -= add_bits;
+
+ if (extraBits < 0)
+ extraBits = 0;
+
+ *targ_bits = targBits;
+ *extra_bits = extraBits;
+}
+
+/*
+ ResvAdjust:
+ Called after a granule's bit allocation. Readjusts the size of
+ the reservoir to reflect the granule's usage.
+*/
+void
+ResvAdjust(lame_internal_flags * gfc, gr_info const *gi)
+{
+ gfc->sv_enc.ResvSize -= gi->part2_3_length + gi->part2_length;
+}
+
+
+/*
+ ResvFrameEnd:
+ Called after all granules in a frame have been allocated. Makes sure
+ that the reservoir size is within limits, possibly by adding stuffing
+ bits.
+*/
+void
+ResvFrameEnd(lame_internal_flags * gfc, int mean_bits)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *const esv = &gfc->sv_enc;
+ III_side_info_t *const l3_side = &gfc->l3_side;
+ int stuffingBits;
+ int over_bits;
+
+ esv->ResvSize += mean_bits * cfg->mode_gr;
+ stuffingBits = 0;
+ l3_side->resvDrain_post = 0;
+ l3_side->resvDrain_pre = 0;
+
+ /* we must be byte aligned */
+ if ((over_bits = esv->ResvSize % 8) != 0)
+ stuffingBits += over_bits;
+
+
+ over_bits = (esv->ResvSize - stuffingBits) - esv->ResvMax;
+ if (over_bits > 0) {
+ assert(0 == over_bits % 8);
+ assert(over_bits >= 0);
+ stuffingBits += over_bits;
+ }
+
+
+ /* NOTE: enabling the NEW_DRAIN code fixes some problems with FhG decoder
+ shipped with MS Windows operating systems. Using this, it is even
+ possible to use Gabriel's lax buffer consideration again, which
+ assumes, any decoder should have a buffer large enough
+ for a 320 kbps frame at 32 kHz sample rate.
+
+ old drain code:
+ lame -b320 BlackBird.wav ---> does not play with GraphEdit.exe using FhG decoder V1.5 Build 50
+
+ new drain code:
+ lame -b320 BlackBird.wav ---> plays fine with GraphEdit.exe using FhG decoder V1.5 Build 50
+
+ Robert Hegemann, 2010-02-13.
+ */
+ /* drain as many bits as possible into previous frame ancillary data
+ * In particular, in VBR mode ResvMax may have changed, and we have
+ * to make sure main_data_begin does not create a reservoir bigger
+ * than ResvMax mt 4/00*/
+ {
+ int mdb_bytes = Min(l3_side->main_data_begin * 8, stuffingBits) / 8;
+ l3_side->resvDrain_pre += 8 * mdb_bytes;
+ stuffingBits -= 8 * mdb_bytes;
+ esv->ResvSize -= 8 * mdb_bytes;
+ l3_side->main_data_begin -= mdb_bytes;
+ }
+ /* drain the rest into this frames ancillary data */
+ l3_side->resvDrain_post += stuffingBits;
+ esv->ResvSize -= stuffingBits;
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/reservoir.h b/libnative/src/main/cpp/module/mp3/lame/reservoir.h
new file mode 100644
index 0000000000000000000000000000000000000000..7c585936466d296651d8aeb7e7625e3876b0acc7
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/reservoir.h
@@ -0,0 +1,31 @@
+/*
+ * bit reservoir include file
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_RESERVOIR_H
+#define LAME_RESERVOIR_H
+
+int ResvFrameBegin(lame_internal_flags * gfc, int *mean_bits);
+void ResvMaxBits(lame_internal_flags * gfc, int mean_bits, int *targ_bits, int *max_bits,
+ int cbr);
+void ResvAdjust(lame_internal_flags * gfc, gr_info const *gi);
+void ResvFrameEnd(lame_internal_flags * gfc, int mean_bits);
+
+#endif /* LAME_RESERVOIR_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/set_get.c b/libnative/src/main/cpp/module/mp3/lame/set_get.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c61f48f10db929963ab8cb551b9cf9e45f74f1f
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/set_get.c
@@ -0,0 +1,2277 @@
+/* -*- mode: C; mode: fold -*- */
+/*
+ * set/get functions for lame_global_flags
+ *
+ * Copyright (c) 2001-2005 Alexander Leidinger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: set_get.c,v 1.98 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "bitstream.h" /* because of compute_flushbits */
+
+#include "set_get.h"
+#include "lame_global_flags.h"
+
+/*
+ * input stream description
+ */
+
+
+/* number of samples */
+/* it's unlikely for this function to return an error */
+int
+lame_set_num_samples(lame_global_flags * gfp, unsigned long num_samples)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 2^32-1 */
+ gfp->num_samples = num_samples;
+ return 0;
+ }
+ return -1;
+}
+
+unsigned long
+lame_get_num_samples(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->num_samples;
+ }
+ return 0;
+}
+
+
+/* input samplerate */
+int
+lame_set_in_samplerate(lame_global_flags * gfp, int in_samplerate)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* input sample rate in Hz, default = 44100 Hz */
+ gfp->samplerate_in = in_samplerate;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_in_samplerate(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->samplerate_in;
+ }
+ return 0;
+}
+
+
+/* number of channels in input stream */
+int
+lame_set_num_channels(lame_global_flags * gfp, int num_channels)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 2 */
+ if (2 < num_channels || 0 == num_channels) {
+ return -1; /* we don't support more than 2 channels */
+ }
+ gfp->num_channels = num_channels;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_num_channels(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->num_channels;
+ }
+ return 0;
+}
+
+
+/* scale the input by this amount before encoding (not used for decoding) */
+int
+lame_set_scale(lame_global_flags * gfp, float scale)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 */
+ gfp->scale = scale;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_scale(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->scale;
+ }
+ return 0;
+}
+
+
+/* scale the channel 0 (left) input by this amount before
+ encoding (not used for decoding) */
+int
+lame_set_scale_left(lame_global_flags * gfp, float scale)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 */
+ gfp->scale_left = scale;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_scale_left(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->scale_left;
+ }
+ return 0;
+}
+
+
+/* scale the channel 1 (right) input by this amount before
+ encoding (not used for decoding) */
+int
+lame_set_scale_right(lame_global_flags * gfp, float scale)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 */
+ gfp->scale_right = scale;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_scale_right(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->scale_right;
+ }
+ return 0;
+}
+
+
+/* output sample rate in Hz */
+int
+lame_set_out_samplerate(lame_global_flags * gfp, int out_samplerate)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /*
+ * default = 0: LAME picks best value based on the amount
+ * of compression
+ * MPEG only allows:
+ * MPEG1 32, 44.1, 48khz
+ * MPEG2 16, 22.05, 24
+ * MPEG2.5 8, 11.025, 12
+ *
+ * (not used by decoding routines)
+ */
+ gfp->samplerate_out = out_samplerate;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_out_samplerate(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->samplerate_out;
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * general control parameters
+ */
+
+/* collect data for an MP3 frame analzyer */
+int
+lame_set_analysis(lame_global_flags * gfp, int analysis)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > analysis || 1 < analysis)
+ return -1;
+ gfp->analysis = analysis;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_analysis(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->analysis && 1 >= gfp->analysis);
+ return gfp->analysis;
+ }
+ return 0;
+}
+
+
+/* write a Xing VBR header frame */
+int
+lame_set_bWriteVbrTag(lame_global_flags * gfp, int bWriteVbrTag)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 (on) for VBR/ABR modes, 0 (off) for CBR mode */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > bWriteVbrTag || 1 < bWriteVbrTag)
+ return -1;
+ gfp->write_lame_tag = bWriteVbrTag;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_bWriteVbrTag(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->write_lame_tag && 1 >= gfp->write_lame_tag);
+ return gfp->write_lame_tag;
+ }
+ return 0;
+}
+
+
+
+/* decode only, use lame/mpglib to convert mp3 to wav */
+int
+lame_set_decode_only(lame_global_flags * gfp, int decode_only)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > decode_only || 1 < decode_only)
+ return -1;
+ gfp->decode_only = decode_only;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_decode_only(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->decode_only && 1 >= gfp->decode_only);
+ return gfp->decode_only;
+ }
+ return 0;
+}
+
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+/* 1=encode a Vorbis .ogg file. default=0 */
+/* DEPRECATED */
+int CDECL lame_set_ogg(lame_global_flags *, int);
+int CDECL lame_get_ogg(const lame_global_flags *);
+#else
+#endif
+
+/* encode a Vorbis .ogg file */
+/* DEPRECATED */
+int
+lame_set_ogg(lame_global_flags * gfp, int ogg)
+{
+ (void) gfp;
+ (void) ogg;
+ return -1;
+}
+
+int
+lame_get_ogg(const lame_global_flags * gfp)
+{
+ (void) gfp;
+ return 0;
+}
+
+
+/*
+ * Internal algorithm selection.
+ * True quality is determined by the bitrate but this variable will effect
+ * quality by selecting expensive or cheap algorithms.
+ * quality=0..9. 0=best (very slow). 9=worst.
+ * recommended: 3 near-best quality, not too slow
+ * 5 good quality, fast
+ * 7 ok quality, really fast
+ */
+int
+lame_set_quality(lame_global_flags * gfp, int quality)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ if (quality < 0) {
+ gfp->quality = 0;
+ }
+ else if (quality > 9) {
+ gfp->quality = 9;
+ }
+ else {
+ gfp->quality = quality;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_quality(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->quality;
+ }
+ return 0;
+}
+
+
+/* mode = STEREO, JOINT_STEREO, DUAL_CHANNEL (not supported), MONO */
+int
+lame_set_mode(lame_global_flags * gfp, MPEG_mode mode)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ int mpg_mode = mode;
+ /* default: lame chooses based on compression ratio and input channels */
+ if (mpg_mode < 0 || MAX_INDICATOR <= mpg_mode)
+ return -1; /* Unknown MPEG mode! */
+ gfp->mode = mode;
+ return 0;
+ }
+ return -1;
+}
+
+MPEG_mode
+lame_get_mode(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(gfp->mode < MAX_INDICATOR);
+ return gfp->mode;
+ }
+ return NOT_SET;
+}
+
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+/*
+ mode_automs. Use a M/S mode with a switching threshold based on
+ compression ratio
+ DEPRECATED
+*/
+int CDECL lame_set_mode_automs(lame_global_flags *, int);
+int CDECL lame_get_mode_automs(const lame_global_flags *);
+#else
+#endif
+
+/* Us a M/S mode with a switching threshold based on compression ratio */
+/* DEPRECATED */
+int
+lame_set_mode_automs(lame_global_flags * gfp, int mode_automs)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > mode_automs || 1 < mode_automs)
+ return -1;
+ lame_set_mode(gfp, JOINT_STEREO);
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_mode_automs(const lame_global_flags * gfp)
+{
+ (void) gfp;
+ return 1;
+}
+
+
+/*
+ * Force M/S for all frames. For testing only.
+ * Requires mode = 1.
+ */
+int
+lame_set_force_ms(lame_global_flags * gfp, int force_ms)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > force_ms || 1 < force_ms)
+ return -1;
+ gfp->force_ms = force_ms;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_force_ms(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->force_ms && 1 >= gfp->force_ms);
+ return gfp->force_ms;
+ }
+ return 0;
+}
+
+
+/* Use free_format. */
+int
+lame_set_free_format(lame_global_flags * gfp, int free_format)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > free_format || 1 < free_format)
+ return -1;
+ gfp->free_format = free_format;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_free_format(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->free_format && 1 >= gfp->free_format);
+ return gfp->free_format;
+ }
+ return 0;
+}
+
+
+
+/* Perform ReplayGain analysis */
+int
+lame_set_findReplayGain(lame_global_flags * gfp, int findReplayGain)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > findReplayGain || 1 < findReplayGain)
+ return -1;
+ gfp->findReplayGain = findReplayGain;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_findReplayGain(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->findReplayGain && 1 >= gfp->findReplayGain);
+ return gfp->findReplayGain;
+ }
+ return 0;
+}
+
+
+/* Decode on the fly. Find the peak sample. If ReplayGain analysis is
+ enabled then perform it on the decoded data. */
+int
+lame_set_decode_on_the_fly(lame_global_flags * gfp, int decode_on_the_fly)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+#ifndef DECODE_ON_THE_FLY
+ return -1;
+#else
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > decode_on_the_fly || 1 < decode_on_the_fly)
+ return -1;
+
+ gfp->decode_on_the_fly = decode_on_the_fly;
+
+ return 0;
+#endif
+ }
+ return -1;
+}
+
+int
+lame_get_decode_on_the_fly(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->decode_on_the_fly && 1 >= gfp->decode_on_the_fly);
+ return gfp->decode_on_the_fly;
+ }
+ return 0;
+}
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+/* DEPRECATED: now does the same as lame_set_findReplayGain()
+ default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_input(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_input(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as
+ lame_set_decode_on_the_fly() && lame_set_findReplayGain()
+ default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_decode(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as lame_set_decode_on_the_fly()
+ default = 0 (disabled) */
+int CDECL lame_set_findPeakSample(lame_global_flags *, int);
+int CDECL lame_get_findPeakSample(const lame_global_flags *);
+#else
+#endif
+
+/* DEPRECATED. same as lame_set_decode_on_the_fly() */
+int
+lame_set_findPeakSample(lame_global_flags * gfp, int arg)
+{
+ return lame_set_decode_on_the_fly(gfp, arg);
+}
+
+int
+lame_get_findPeakSample(const lame_global_flags * gfp)
+{
+ return lame_get_decode_on_the_fly(gfp);
+}
+
+/* DEPRECATED. same as lame_set_findReplayGain() */
+int
+lame_set_ReplayGain_input(lame_global_flags * gfp, int arg)
+{
+ return lame_set_findReplayGain(gfp, arg);
+}
+
+int
+lame_get_ReplayGain_input(const lame_global_flags * gfp)
+{
+ return lame_get_findReplayGain(gfp);
+}
+
+/* DEPRECATED. same as lame_set_decode_on_the_fly() &&
+ lame_set_findReplayGain() */
+int
+lame_set_ReplayGain_decode(lame_global_flags * gfp, int arg)
+{
+ if (lame_set_decode_on_the_fly(gfp, arg) < 0 || lame_set_findReplayGain(gfp, arg) < 0)
+ return -1;
+ else
+ return 0;
+}
+
+int
+lame_get_ReplayGain_decode(const lame_global_flags * gfp)
+{
+ if (lame_get_decode_on_the_fly(gfp) > 0 && lame_get_findReplayGain(gfp) > 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* set and get some gapless encoding flags */
+
+int
+lame_set_nogap_total(lame_global_flags * gfp, int the_nogap_total)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->nogap_total = the_nogap_total;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_nogap_total(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->nogap_total;
+ }
+ return 0;
+}
+
+int
+lame_set_nogap_currentindex(lame_global_flags * gfp, int the_nogap_index)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->nogap_current = the_nogap_index;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_nogap_currentindex(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->nogap_current;
+ }
+ return 0;
+}
+
+
+/* message handlers */
+int
+lame_set_errorf(lame_global_flags * gfp, void (*func) (const char *, va_list))
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->report.errorf = func;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_set_debugf(lame_global_flags * gfp, void (*func) (const char *, va_list))
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->report.debugf = func;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_set_msgf(lame_global_flags * gfp, void (*func) (const char *, va_list))
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->report.msgf = func;
+ return 0;
+ }
+ return -1;
+}
+
+
+/*
+ * Set one of
+ * - brate
+ * - compression ratio.
+ *
+ * Default is compression ratio of 11.
+ */
+int
+lame_set_brate(lame_global_flags * gfp, int brate)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->brate = brate;
+ if (brate > 320) {
+ gfp->disable_reservoir = 1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_brate(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->brate;
+ }
+ return 0;
+}
+
+int
+lame_set_compression_ratio(lame_global_flags * gfp, float compression_ratio)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->compression_ratio = compression_ratio;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_compression_ratio(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->compression_ratio;
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * frame parameters
+ */
+
+/* Mark as copyright protected. */
+int
+lame_set_copyright(lame_global_flags * gfp, int copyright)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > copyright || 1 < copyright)
+ return -1;
+ gfp->copyright = copyright;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_copyright(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->copyright && 1 >= gfp->copyright);
+ return gfp->copyright;
+ }
+ return 0;
+}
+
+
+/* Mark as original. */
+int
+lame_set_original(lame_global_flags * gfp, int original)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 (enabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > original || 1 < original)
+ return -1;
+ gfp->original = original;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_original(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->original && 1 >= gfp->original);
+ return gfp->original;
+ }
+ return 0;
+}
+
+
+/*
+ * error_protection.
+ * Use 2 bytes from each frame for CRC checksum.
+ */
+int
+lame_set_error_protection(lame_global_flags * gfp, int error_protection)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > error_protection || 1 < error_protection)
+ return -1;
+ gfp->error_protection = error_protection;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_error_protection(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->error_protection && 1 >= gfp->error_protection);
+ return gfp->error_protection;
+ }
+ return 0;
+}
+
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */
+int CDECL lame_set_padding_type(lame_global_flags *, Padding_type);
+Padding_type CDECL lame_get_padding_type(const lame_global_flags *);
+#else
+#endif
+
+/*
+ * padding_type.
+ * PAD_NO = pad no frames
+ * PAD_ALL = pad all frames
+ * PAD_ADJUST = adjust padding
+ */
+int
+lame_set_padding_type(lame_global_flags * gfp, Padding_type padding_type)
+{
+ (void) gfp;
+ (void) padding_type;
+ return 0;
+}
+
+Padding_type
+lame_get_padding_type(const lame_global_flags * gfp)
+{
+ (void) gfp;
+ return PAD_ADJUST;
+}
+
+
+/* MP3 'private extension' bit. Meaningless. */
+int
+lame_set_extension(lame_global_flags * gfp, int extension)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > extension || 1 < extension)
+ return -1;
+ gfp->extension = extension;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_extension(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->extension && 1 >= gfp->extension);
+ return gfp->extension;
+ }
+ return 0;
+}
+
+
+/* Enforce strict ISO compliance. */
+int
+lame_set_strict_ISO(lame_global_flags * gfp, int val)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (val < MDB_DEFAULT || MDB_MAXIMUM < val)
+ return -1;
+ gfp->strict_ISO = val;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_strict_ISO(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->strict_ISO;
+ }
+ return 0;
+}
+
+
+
+
+/********************************************************************
+ * quantization/noise shaping
+ ***********************************************************************/
+
+/* Disable the bit reservoir. For testing only. */
+int
+lame_set_disable_reservoir(lame_global_flags * gfp, int disable_reservoir)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > disable_reservoir || 1 < disable_reservoir)
+ return -1;
+ gfp->disable_reservoir = disable_reservoir;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_disable_reservoir(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->disable_reservoir && 1 >= gfp->disable_reservoir);
+ return gfp->disable_reservoir;
+ }
+ return 0;
+}
+
+
+
+
+int
+lame_set_experimentalX(lame_global_flags * gfp, int experimentalX)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_set_quant_comp(gfp, experimentalX);
+ lame_set_quant_comp_short(gfp, experimentalX);
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_experimentalX(const lame_global_flags * gfp)
+{
+ return lame_get_quant_comp(gfp);
+}
+
+
+/* Select a different "best quantization" function. default = 0 */
+int
+lame_set_quant_comp(lame_global_flags * gfp, int quant_type)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->quant_comp = quant_type;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_quant_comp(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->quant_comp;
+ }
+ return 0;
+}
+
+
+/* Select a different "best quantization" function. default = 0 */
+int
+lame_set_quant_comp_short(lame_global_flags * gfp, int quant_type)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->quant_comp_short = quant_type;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_quant_comp_short(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->quant_comp_short;
+ }
+ return 0;
+}
+
+
+/* Another experimental option. For testing only. */
+int
+lame_set_experimentalY(lame_global_flags * gfp, int experimentalY)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->experimentalY = experimentalY;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_experimentalY(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->experimentalY;
+ }
+ return 0;
+}
+
+
+int
+lame_set_experimentalZ(lame_global_flags * gfp, int experimentalZ)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->experimentalZ = experimentalZ;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_experimentalZ(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->experimentalZ;
+ }
+ return 0;
+}
+
+
+/* Naoki's psycho acoustic model. */
+int
+lame_set_exp_nspsytune(lame_global_flags * gfp, int exp_nspsytune)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+ gfp->exp_nspsytune = exp_nspsytune;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_exp_nspsytune(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->exp_nspsytune;
+ }
+ return 0;
+}
+
+
+
+
+/********************************************************************
+ * VBR control
+ ***********************************************************************/
+
+/* Types of VBR. default = vbr_off = CBR */
+int
+lame_set_VBR(lame_global_flags * gfp, vbr_mode VBR)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ int vbr_q = VBR;
+ if (0 > vbr_q || vbr_max_indicator <= vbr_q)
+ return -1; /* Unknown VBR mode! */
+ gfp->VBR = VBR;
+ return 0;
+ }
+ return -1;
+}
+
+vbr_mode
+lame_get_VBR(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(gfp->VBR < vbr_max_indicator);
+ return gfp->VBR;
+ }
+ return vbr_off;
+}
+
+
+/*
+ * VBR quality level.
+ * 0 = highest
+ * 9 = lowest
+ */
+int
+lame_set_VBR_q(lame_global_flags * gfp, int VBR_q)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ int ret = 0;
+
+ if (0 > VBR_q) {
+ ret = -1; /* Unknown VBR quality level! */
+ VBR_q = 0;
+ }
+ if (9 < VBR_q) {
+ ret = -1;
+ VBR_q = 9;
+ }
+ gfp->VBR_q = VBR_q;
+ gfp->VBR_q_frac = 0;
+ return ret;
+ }
+ return -1;
+}
+
+int
+lame_get_VBR_q(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->VBR_q && 10 > gfp->VBR_q);
+ return gfp->VBR_q;
+ }
+ return 0;
+}
+
+int
+lame_set_VBR_quality(lame_global_flags * gfp, float VBR_q)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ int ret = 0;
+
+ if (0 > VBR_q) {
+ ret = -1; /* Unknown VBR quality level! */
+ VBR_q = 0;
+ }
+ if (9.999 < VBR_q) {
+ ret = -1;
+ VBR_q = 9.999;
+ }
+
+ gfp->VBR_q = (int) VBR_q;
+ gfp->VBR_q_frac = VBR_q - gfp->VBR_q;
+
+ return ret;
+ }
+ return -1;
+}
+
+float
+lame_get_VBR_quality(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->VBR_q + gfp->VBR_q_frac;
+ }
+ return 0;
+}
+
+
+/* Ignored except for VBR = vbr_abr (ABR mode) */
+int
+lame_set_VBR_mean_bitrate_kbps(lame_global_flags * gfp, int VBR_mean_bitrate_kbps)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->VBR_mean_bitrate_kbps = VBR_mean_bitrate_kbps;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_VBR_mean_bitrate_kbps(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->VBR_mean_bitrate_kbps;
+ }
+ return 0;
+}
+
+int
+lame_set_VBR_min_bitrate_kbps(lame_global_flags * gfp, int VBR_min_bitrate_kbps)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->VBR_min_bitrate_kbps = VBR_min_bitrate_kbps;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_VBR_min_bitrate_kbps(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->VBR_min_bitrate_kbps;
+ }
+ return 0;
+}
+
+int
+lame_set_VBR_max_bitrate_kbps(lame_global_flags * gfp, int VBR_max_bitrate_kbps)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->VBR_max_bitrate_kbps = VBR_max_bitrate_kbps;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_VBR_max_bitrate_kbps(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->VBR_max_bitrate_kbps;
+ }
+ return 0;
+}
+
+
+/*
+ * Strictly enforce VBR_min_bitrate.
+ * Normally it will be violated for analog silence.
+ */
+int
+lame_set_VBR_hard_min(lame_global_flags * gfp, int VBR_hard_min)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 (disabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > VBR_hard_min || 1 < VBR_hard_min)
+ return -1;
+
+ gfp->VBR_hard_min = VBR_hard_min;
+
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_VBR_hard_min(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->VBR_hard_min && 1 >= gfp->VBR_hard_min);
+ return gfp->VBR_hard_min;
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * Filtering control
+ ***********************************************************************/
+
+/*
+ * Freqency in Hz to apply lowpass.
+ * 0 = default = lame chooses
+ * -1 = disabled
+ */
+int
+lame_set_lowpassfreq(lame_global_flags * gfp, int lowpassfreq)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->lowpassfreq = lowpassfreq;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_lowpassfreq(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->lowpassfreq;
+ }
+ return 0;
+}
+
+
+/*
+ * Width of transition band (in Hz).
+ * default = one polyphase filter band
+ */
+int
+lame_set_lowpasswidth(lame_global_flags * gfp, int lowpasswidth)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->lowpasswidth = lowpasswidth;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_lowpasswidth(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->lowpasswidth;
+ }
+ return 0;
+}
+
+
+/*
+ * Frequency in Hz to apply highpass.
+ * 0 = default = lame chooses
+ * -1 = disabled
+ */
+int
+lame_set_highpassfreq(lame_global_flags * gfp, int highpassfreq)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->highpassfreq = highpassfreq;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_highpassfreq(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->highpassfreq;
+ }
+ return 0;
+}
+
+
+/*
+ * Width of transition band (in Hz).
+ * default = one polyphase filter band
+ */
+int
+lame_set_highpasswidth(lame_global_flags * gfp, int highpasswidth)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->highpasswidth = highpasswidth;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_highpasswidth(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->highpasswidth;
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * psycho acoustics and other arguments which you should not change
+ * unless you know what you are doing
+ */
+
+
+/* Adjust masking values. */
+int
+lame_set_maskingadjust(lame_global_flags * gfp, float adjust)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->maskingadjust = adjust;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_maskingadjust(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->maskingadjust;
+ }
+ return 0;
+}
+
+int
+lame_set_maskingadjust_short(lame_global_flags * gfp, float adjust)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->maskingadjust_short = adjust;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_maskingadjust_short(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->maskingadjust_short;
+ }
+ return 0;
+}
+
+/* Only use ATH for masking. */
+int
+lame_set_ATHonly(lame_global_flags * gfp, int ATHonly)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->ATHonly = ATHonly;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_ATHonly(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->ATHonly;
+ }
+ return 0;
+}
+
+
+/* Only use ATH for short blocks. */
+int
+lame_set_ATHshort(lame_global_flags * gfp, int ATHshort)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->ATHshort = ATHshort;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_ATHshort(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->ATHshort;
+ }
+ return 0;
+}
+
+
+/* Disable ATH. */
+int
+lame_set_noATH(lame_global_flags * gfp, int noATH)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->noATH = noATH;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_noATH(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->noATH;
+ }
+ return 0;
+}
+
+
+/* Select ATH formula. */
+int
+lame_set_ATHtype(lame_global_flags * gfp, int ATHtype)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* XXX: ATHtype should be converted to an enum. */
+ gfp->ATHtype = ATHtype;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_ATHtype(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->ATHtype;
+ }
+ return 0;
+}
+
+
+/* Select ATH formula 4 shape. */
+int
+lame_set_ATHcurve(lame_global_flags * gfp, float ATHcurve)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->ATHcurve = ATHcurve;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_ATHcurve(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->ATHcurve;
+ }
+ return 0;
+}
+
+
+/* Lower ATH by this many db. */
+int
+lame_set_ATHlower(lame_global_flags * gfp, float ATHlower)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->ATH_lower_db = ATHlower;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_ATHlower(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->ATH_lower_db;
+ }
+ return 0;
+}
+
+
+/* Select ATH adaptive adjustment scheme. */
+int
+lame_set_athaa_type(lame_global_flags * gfp, int athaa_type)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->athaa_type = athaa_type;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_athaa_type(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->athaa_type;
+ }
+ return 0;
+}
+
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+int CDECL lame_set_athaa_loudapprox(lame_global_flags * gfp, int athaa_loudapprox);
+int CDECL lame_get_athaa_loudapprox(const lame_global_flags * gfp);
+#else
+#endif
+
+/* Select the loudness approximation used by the ATH adaptive auto-leveling. */
+int
+lame_set_athaa_loudapprox(lame_global_flags * gfp, int athaa_loudapprox)
+{
+ (void) gfp;
+ (void) athaa_loudapprox;
+ return 0;
+}
+
+int
+lame_get_athaa_loudapprox(const lame_global_flags * gfp)
+{
+ (void) gfp;
+ /* obsolete, the type known under number 2 is the only survival */
+ return 2;
+}
+
+
+/* Adjust (in dB) the point below which adaptive ATH level adjustment occurs. */
+int
+lame_set_athaa_sensitivity(lame_global_flags * gfp, float athaa_sensitivity)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->athaa_sensitivity = athaa_sensitivity;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_athaa_sensitivity(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->athaa_sensitivity;
+ }
+ return 0;
+}
+
+
+/* Predictability limit (ISO tonality formula) */
+int lame_set_cwlimit(lame_global_flags * gfp, int cwlimit);
+int lame_get_cwlimit(const lame_global_flags * gfp);
+
+int
+lame_set_cwlimit(lame_global_flags * gfp, int cwlimit)
+{
+ (void) gfp;
+ (void) cwlimit;
+ return 0;
+}
+
+int
+lame_get_cwlimit(const lame_global_flags * gfp)
+{
+ (void) gfp;
+ return 0;
+}
+
+
+
+/*
+ * Allow blocktypes to differ between channels.
+ * default:
+ * 0 for jstereo => block types coupled
+ * 1 for stereo => block types may differ
+ */
+int
+lame_set_allow_diff_short(lame_global_flags * gfp, int allow_diff_short)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->short_blocks = allow_diff_short ? short_block_allowed : short_block_coupled;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_allow_diff_short(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ if (gfp->short_blocks == short_block_allowed)
+ return 1; /* short blocks allowed to differ */
+ else
+ return 0; /* not set, dispensed, forced or coupled */
+ }
+ return 0;
+}
+
+
+/* Use temporal masking effect */
+int
+lame_set_useTemporal(lame_global_flags * gfp, int useTemporal)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 1 (enabled) */
+
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 <= useTemporal && useTemporal <= 1) {
+ gfp->useTemporal = useTemporal;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+lame_get_useTemporal(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->useTemporal && 1 >= gfp->useTemporal);
+ return gfp->useTemporal;
+ }
+ return 0;
+}
+
+
+/* Use inter-channel masking effect */
+int
+lame_set_interChRatio(lame_global_flags * gfp, float ratio)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0.0 (no inter-channel maskin) */
+ if (0 <= ratio && ratio <= 1.0) {
+ gfp->interChRatio = ratio;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+float
+lame_get_interChRatio(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert((0 <= gfp->interChRatio && gfp->interChRatio <= 1.0) || EQ(gfp->interChRatio, -1));
+ return gfp->interChRatio;
+ }
+ return 0;
+}
+
+
+/* Use pseudo substep shaping method */
+int
+lame_set_substep(lame_global_flags * gfp, int method)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0.0 (no substep noise shaping) */
+ if (0 <= method && method <= 7) {
+ gfp->substep_shaping = method;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+lame_get_substep(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->substep_shaping && gfp->substep_shaping <= 7);
+ return gfp->substep_shaping;
+ }
+ return 0;
+}
+
+/* scalefactors scale */
+int
+lame_set_sfscale(lame_global_flags * gfp, int val)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->noise_shaping = (val != 0) ? 2 : 1;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_sfscale(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return (gfp->noise_shaping == 2) ? 1 : 0;
+ }
+ return 0;
+}
+
+/* subblock gain */
+int
+lame_set_subblock_gain(lame_global_flags * gfp, int sbgain)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->subblock_gain = sbgain;
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_subblock_gain(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->subblock_gain;
+ }
+ return 0;
+}
+
+
+/* Disable short blocks. */
+int
+lame_set_no_short_blocks(lame_global_flags * gfp, int no_short_blocks)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 <= no_short_blocks && no_short_blocks <= 1) {
+ gfp->short_blocks = no_short_blocks ? short_block_dispensed : short_block_allowed;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+lame_get_no_short_blocks(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ switch (gfp->short_blocks) {
+ default:
+ case short_block_not_set:
+ return -1;
+ case short_block_dispensed:
+ return 1;
+ case short_block_allowed:
+ case short_block_coupled:
+ case short_block_forced:
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+/* Force short blocks. */
+int
+lame_set_force_short_blocks(lame_global_flags * gfp, int short_blocks)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* enforce disable/enable meaning, if we need more than two values
+ we need to switch to an enum to have an apropriate representation
+ of the possible meanings of the value */
+ if (0 > short_blocks || 1 < short_blocks)
+ return -1;
+
+ if (short_blocks == 1)
+ gfp->short_blocks = short_block_forced;
+ else if (gfp->short_blocks == short_block_forced)
+ gfp->short_blocks = short_block_allowed;
+
+ return 0;
+ }
+ return -1;
+}
+
+int
+lame_get_force_short_blocks(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ switch (gfp->short_blocks) {
+ default:
+ case short_block_not_set:
+ return -1;
+ case short_block_dispensed:
+ case short_block_allowed:
+ case short_block_coupled:
+ return 0;
+ case short_block_forced:
+ return 1;
+ }
+ }
+ return -1;
+}
+
+int
+lame_set_short_threshold_lrm(lame_global_flags * gfp, float lrm)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->attackthre = lrm;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_short_threshold_lrm(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->attackthre;
+ }
+ return 0;
+}
+
+int
+lame_set_short_threshold_s(lame_global_flags * gfp, float s)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->attackthre_s = s;
+ return 0;
+ }
+ return -1;
+}
+
+float
+lame_get_short_threshold_s(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->attackthre_s;
+ }
+ return 0;
+}
+
+int
+lame_set_short_threshold(lame_global_flags * gfp, float lrm, float s)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_set_short_threshold_lrm(gfp, lrm);
+ lame_set_short_threshold_s(gfp, s);
+ return 0;
+ }
+ return -1;
+}
+
+
+/*
+ * Input PCM is emphased PCM
+ * (for instance from one of the rarely emphased CDs).
+ *
+ * It is STRONGLY not recommended to use this, because psycho does not
+ * take it into account, and last but not least many decoders
+ * ignore these bits
+ */
+int
+lame_set_emphasis(lame_global_flags * gfp, int emphasis)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* XXX: emphasis should be converted to an enum */
+ if (0 <= emphasis && emphasis < 4) {
+ gfp->emphasis = emphasis;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+lame_get_emphasis(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ assert(0 <= gfp->emphasis && gfp->emphasis < 4);
+ return gfp->emphasis;
+ }
+ return 0;
+}
+
+
+
+
+/***************************************************************/
+/* internal variables, cannot be set... */
+/* provided because they may be of use to calling application */
+/***************************************************************/
+
+/* MPEG version.
+ * 0 = MPEG-2
+ * 1 = MPEG-1
+ * (2 = MPEG-2.5)
+ */
+int
+lame_get_version(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->cfg.version;
+ }
+ }
+ return 0;
+}
+
+
+/* Encoder delay. */
+int
+lame_get_encoder_delay(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_enc.encoder_delay;
+ }
+ }
+ return 0;
+}
+
+/* padding added to the end of the input */
+int
+lame_get_encoder_padding(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_enc.encoder_padding;
+ }
+ }
+ return 0;
+}
+
+
+/* Size of MPEG frame. */
+int
+lame_get_framesize(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ return 576 * cfg->mode_gr;
+ }
+ }
+ return 0;
+}
+
+
+/* Number of frames encoded so far. */
+int
+lame_get_frameNum(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_enc.frame_number;
+ }
+ }
+ return 0;
+}
+
+int
+lame_get_mf_samples_to_encode(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->sv_enc.mf_samples_to_encode;
+ }
+ }
+ return 0;
+}
+
+int CDECL
+lame_get_size_mp3buffer(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ int size;
+ compute_flushbits(gfc, &size);
+ return size;
+ }
+ }
+ return 0;
+}
+
+int
+lame_get_RadioGain(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_rpg.RadioGain;
+ }
+ }
+ return 0;
+}
+
+int
+lame_get_AudiophileGain(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+float
+lame_get_PeakSample(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return (float) gfc->ov_rpg.PeakSample;
+ }
+ }
+ return 0;
+}
+
+int
+lame_get_noclipGainChange(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_rpg.noclipGainChange;
+ }
+ }
+ return 0;
+}
+
+float
+lame_get_noclipScale(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ return gfc->ov_rpg.noclipScale;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * LAME's estimate of the total number of frames to be encoded.
+ * Only valid if calling program set num_samples.
+ */
+int
+lame_get_totalframes(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ lame_internal_flags const *const gfc = gfp->internal_flags;
+ if (is_lame_internal_flags_valid(gfc)) {
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ unsigned long const pcm_samples_per_frame = 576 * cfg->mode_gr;
+ unsigned long pcm_samples_to_encode = gfp->num_samples;
+ unsigned long end_padding = 0;
+
+ /* estimate based on user set num_samples: */
+ if (pcm_samples_to_encode == (0ul-1ul)) {
+ return 0;
+ }
+ if (gfp->samplerate_in != gfp->samplerate_out && gfp->samplerate_in > 0) {
+ double const q = (double)gfp->samplerate_out / gfp->samplerate_in;
+ pcm_samples_to_encode *= q;
+ }
+ pcm_samples_to_encode += 576;
+ end_padding = pcm_samples_per_frame - (pcm_samples_to_encode % pcm_samples_per_frame);
+ if (end_padding < 576) {
+ end_padding += pcm_samples_per_frame;
+ }
+ pcm_samples_to_encode += end_padding;
+ /* check to see if we underestimated totalframes */
+ /* if (totalframes < gfp->frameNum) */
+ /* totalframes = gfp->frameNum; */
+ return pcm_samples_to_encode / pcm_samples_per_frame;
+ }
+ }
+ return 0;
+}
+
+
+
+
+
+int
+lame_set_preset(lame_global_flags * gfp, int preset)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->preset = preset;
+ return apply_preset(gfp, preset, 1);
+ }
+ return -1;
+}
+
+
+
+int
+lame_set_asm_optimizations(lame_global_flags * gfp, int optim, int mode)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ mode = (mode == 1 ? 1 : 0);
+ switch (optim) {
+ case MMX:{
+ gfp->asm_optimizations.mmx = mode;
+ return optim;
+ }
+ case AMD_3DNOW:{
+ gfp->asm_optimizations.amd3dnow = mode;
+ return optim;
+ }
+ case SSE:{
+ gfp->asm_optimizations.sse = mode;
+ return optim;
+ }
+ default:
+ return optim;
+ }
+ }
+ return -1;
+}
+
+
+void
+lame_set_write_id3tag_automatic(lame_global_flags * gfp, int v)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->write_id3tag_automatic = v;
+ }
+}
+
+
+int
+lame_get_write_id3tag_automatic(lame_global_flags const *gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->write_id3tag_automatic;
+ }
+ return 1;
+}
+
+
+/*
+
+UNDOCUMENTED, experimental settings. These routines are not prototyped
+in lame.h. You should not use them, they are experimental and may
+change.
+
+*/
+
+
+/*
+ * just another daily changing developer switch
+ */
+void CDECL lame_set_tune(lame_global_flags *, float);
+
+void
+lame_set_tune(lame_global_flags * gfp, float val)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ gfp->tune_value_a = val;
+ gfp->tune = 1;
+ }
+}
+
+/* Custom msfix hack */
+void
+lame_set_msfix(lame_global_flags * gfp, double msfix)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ /* default = 0 */
+ gfp->msfix = msfix;
+ }
+}
+
+float
+lame_get_msfix(const lame_global_flags * gfp)
+{
+ if (is_lame_global_flags_valid(gfp)) {
+ return gfp->msfix;
+ }
+ return 0;
+}
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+int CDECL lame_set_preset_expopts(lame_global_flags *, int);
+#else
+#endif
+
+int
+lame_set_preset_expopts(lame_global_flags * gfp, int preset_expopts)
+{
+ (void) gfp;
+ (void) preset_expopts;
+ return 0;
+}
+
+
+int
+lame_set_preset_notune(lame_global_flags * gfp, int preset_notune)
+{
+ (void) gfp;
+ (void) preset_notune;
+ return 0;
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/set_get.h b/libnative/src/main/cpp/module/mp3/lame/set_get.h
new file mode 100644
index 0000000000000000000000000000000000000000..99ab73cfbe3b2ddd31e7fe50f887bbac7d25f11d
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/set_get.h
@@ -0,0 +1,76 @@
+/*
+ * set_get.h -- Internal set/get definitions
+ *
+ * Copyright (C) 2003 Gabriel Bouvigne / Lame project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SET_GET_H__
+#define __SET_GET_H__
+
+#include "lame.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* select psychoacoustic model */
+
+/* manage short blocks */
+ int CDECL lame_set_short_threshold(lame_global_flags *, float, float);
+ int CDECL lame_set_short_threshold_lrm(lame_global_flags *, float);
+ float CDECL lame_get_short_threshold_lrm(const lame_global_flags *);
+ int CDECL lame_set_short_threshold_s(lame_global_flags *, float);
+ float CDECL lame_get_short_threshold_s(const lame_global_flags *);
+
+
+ int CDECL lame_set_maskingadjust(lame_global_flags *, float);
+ float CDECL lame_get_maskingadjust(const lame_global_flags *);
+
+ int CDECL lame_set_maskingadjust_short(lame_global_flags *, float);
+ float CDECL lame_get_maskingadjust_short(const lame_global_flags *);
+
+/* select ATH formula 4 shape */
+ int CDECL lame_set_ATHcurve(lame_global_flags *, float);
+ float CDECL lame_get_ATHcurve(const lame_global_flags *);
+
+ int CDECL lame_set_preset_notune(lame_global_flags *, int);
+
+/* substep shaping method */
+ int CDECL lame_set_substep(lame_global_flags *, int);
+ int CDECL lame_get_substep(const lame_global_flags *);
+
+/* scalefactors scale */
+ int CDECL lame_set_sfscale(lame_global_flags *, int);
+ int CDECL lame_get_sfscale(const lame_global_flags *);
+
+/* subblock gain */
+ int CDECL lame_set_subblock_gain(lame_global_flags *, int);
+ int CDECL lame_get_subblock_gain(const lame_global_flags *);
+
+
+
+/*presets*/
+ int apply_preset(lame_global_flags *, int preset, int enforce);
+
+ void CDECL lame_set_tune(lame_t, float); /* FOR INTERNAL USE ONLY */
+ void CDECL lame_set_msfix(lame_t gfp, double msfix);
+
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/libnative/src/main/cpp/module/mp3/lame/tables.c b/libnative/src/main/cpp/module/mp3/lame/tables.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0230999f2fec9958051014afec855e6ce6debb5
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/tables.c
@@ -0,0 +1,564 @@
+/*
+ * MPEG layer 3 tables source file
+ *
+ * Copyright (c) 1999 Albert L Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: tables.c,v 1.29 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "machine.h"
+
+#include "lame.h"
+#include "tables.h"
+
+
+static const uint16_t t1HB[] = {
+ 1, 1,
+ 1, 0
+};
+
+static const uint16_t t2HB[] = {
+ 1, 2, 1,
+ 3, 1, 1,
+ 3, 2, 0
+};
+
+static const uint16_t t3HB[] = {
+ 3, 2, 1,
+ 1, 1, 1,
+ 3, 2, 0
+};
+
+static const uint16_t t5HB[] = {
+ 1, 2, 6, 5,
+ 3, 1, 4, 4,
+ 7, 5, 7, 1,
+ 6, 1, 1, 0
+};
+
+static const uint16_t t6HB[] = {
+ 7, 3, 5, 1,
+ 6, 2, 3, 2,
+ 5, 4, 4, 1,
+ 3, 3, 2, 0
+};
+
+static const uint16_t t7HB[] = {
+ 1, 2, 10, 19, 16, 10,
+ 3, 3, 7, 10, 5, 3,
+ 11, 4, 13, 17, 8, 4,
+ 12, 11, 18, 15, 11, 2,
+ 7, 6, 9, 14, 3, 1,
+ 6, 4, 5, 3, 2, 0
+};
+
+static const uint16_t t8HB[] = {
+ 3, 4, 6, 18, 12, 5,
+ 5, 1, 2, 16, 9, 3,
+ 7, 3, 5, 14, 7, 3,
+ 19, 17, 15, 13, 10, 4,
+ 13, 5, 8, 11, 5, 1,
+ 12, 4, 4, 1, 1, 0
+};
+
+static const uint16_t t9HB[] = {
+ 7, 5, 9, 14, 15, 7,
+ 6, 4, 5, 5, 6, 7,
+ 7, 6, 8, 8, 8, 5,
+ 15, 6, 9, 10, 5, 1,
+ 11, 7, 9, 6, 4, 1,
+ 14, 4, 6, 2, 6, 0
+};
+
+static const uint16_t t10HB[] = {
+ 1, 2, 10, 23, 35, 30, 12, 17,
+ 3, 3, 8, 12, 18, 21, 12, 7,
+ 11, 9, 15, 21, 32, 40, 19, 6,
+ 14, 13, 22, 34, 46, 23, 18, 7,
+ 20, 19, 33, 47, 27, 22, 9, 3,
+ 31, 22, 41, 26, 21, 20, 5, 3,
+ 14, 13, 10, 11, 16, 6, 5, 1,
+ 9, 8, 7, 8, 4, 4, 2, 0
+};
+
+static const uint16_t t11HB[] = {
+ 3, 4, 10, 24, 34, 33, 21, 15,
+ 5, 3, 4, 10, 32, 17, 11, 10,
+ 11, 7, 13, 18, 30, 31, 20, 5,
+ 25, 11, 19, 59, 27, 18, 12, 5,
+ 35, 33, 31, 58, 30, 16, 7, 5,
+ 28, 26, 32, 19, 17, 15, 8, 14,
+ 14, 12, 9, 13, 14, 9, 4, 1,
+ 11, 4, 6, 6, 6, 3, 2, 0
+};
+
+static const uint16_t t12HB[] = {
+ 9, 6, 16, 33, 41, 39, 38, 26,
+ 7, 5, 6, 9, 23, 16, 26, 11,
+ 17, 7, 11, 14, 21, 30, 10, 7,
+ 17, 10, 15, 12, 18, 28, 14, 5,
+ 32, 13, 22, 19, 18, 16, 9, 5,
+ 40, 17, 31, 29, 17, 13, 4, 2,
+ 27, 12, 11, 15, 10, 7, 4, 1,
+ 27, 12, 8, 12, 6, 3, 1, 0
+};
+
+static const uint16_t t13HB[] = {
+ 1, 5, 14, 21, 34, 51, 46, 71, 42, 52, 68, 52, 67, 44, 43, 19,
+ 3, 4, 12, 19, 31, 26, 44, 33, 31, 24, 32, 24, 31, 35, 22, 14,
+ 15, 13, 23, 36, 59, 49, 77, 65, 29, 40, 30, 40, 27, 33, 42, 16,
+ 22, 20, 37, 61, 56, 79, 73, 64, 43, 76, 56, 37, 26, 31, 25, 14,
+ 35, 16, 60, 57, 97, 75, 114, 91, 54, 73, 55, 41, 48, 53, 23, 24,
+ 58, 27, 50, 96, 76, 70, 93, 84, 77, 58, 79, 29, 74, 49, 41, 17,
+ 47, 45, 78, 74, 115, 94, 90, 79, 69, 83, 71, 50, 59, 38, 36, 15,
+ 72, 34, 56, 95, 92, 85, 91, 90, 86, 73, 77, 65, 51, 44, 43, 42,
+ 43, 20, 30, 44, 55, 78, 72, 87, 78, 61, 46, 54, 37, 30, 20, 16,
+ 53, 25, 41, 37, 44, 59, 54, 81, 66, 76, 57, 54, 37, 18, 39, 11,
+ 35, 33, 31, 57, 42, 82, 72, 80, 47, 58, 55, 21, 22, 26, 38, 22,
+ 53, 25, 23, 38, 70, 60, 51, 36, 55, 26, 34, 23, 27, 14, 9, 7,
+ 34, 32, 28, 39, 49, 75, 30, 52, 48, 40, 52, 28, 18, 17, 9, 5,
+ 45, 21, 34, 64, 56, 50, 49, 45, 31, 19, 12, 15, 10, 7, 6, 3,
+ 48, 23, 20, 39, 36, 35, 53, 21, 16, 23, 13, 10, 6, 1, 4, 2,
+ 16, 15, 17, 27, 25, 20, 29, 11, 17, 12, 16, 8, 1, 1, 0, 1
+};
+
+static const uint16_t t15HB[] = {
+ 7, 12, 18, 53, 47, 76, 124, 108, 89, 123, 108, 119, 107, 81, 122, 63,
+ 13, 5, 16, 27, 46, 36, 61, 51, 42, 70, 52, 83, 65, 41, 59, 36,
+ 19, 17, 15, 24, 41, 34, 59, 48, 40, 64, 50, 78, 62, 80, 56, 33,
+ 29, 28, 25, 43, 39, 63, 55, 93, 76, 59, 93, 72, 54, 75, 50, 29,
+ 52, 22, 42, 40, 67, 57, 95, 79, 72, 57, 89, 69, 49, 66, 46, 27,
+ 77, 37, 35, 66, 58, 52, 91, 74, 62, 48, 79, 63, 90, 62, 40, 38,
+ 125, 32, 60, 56, 50, 92, 78, 65, 55, 87, 71, 51, 73, 51, 70, 30,
+ 109, 53, 49, 94, 88, 75, 66, 122, 91, 73, 56, 42, 64, 44, 21, 25,
+ 90, 43, 41, 77, 73, 63, 56, 92, 77, 66, 47, 67, 48, 53, 36, 20,
+ 71, 34, 67, 60, 58, 49, 88, 76, 67, 106, 71, 54, 38, 39, 23, 15,
+ 109, 53, 51, 47, 90, 82, 58, 57, 48, 72, 57, 41, 23, 27, 62, 9,
+ 86, 42, 40, 37, 70, 64, 52, 43, 70, 55, 42, 25, 29, 18, 11, 11,
+ 118, 68, 30, 55, 50, 46, 74, 65, 49, 39, 24, 16, 22, 13, 14, 7,
+ 91, 44, 39, 38, 34, 63, 52, 45, 31, 52, 28, 19, 14, 8, 9, 3,
+ 123, 60, 58, 53, 47, 43, 32, 22, 37, 24, 17, 12, 15, 10, 2, 1,
+ 71, 37, 34, 30, 28, 20, 17, 26, 21, 16, 10, 6, 8, 6, 2, 0
+};
+
+static const uint16_t t16HB[] = {
+ 1, 5, 14, 44, 74, 63, 110, 93, 172, 149, 138, 242, 225, 195, 376, 17,
+ 3, 4, 12, 20, 35, 62, 53, 47, 83, 75, 68, 119, 201, 107, 207, 9,
+ 15, 13, 23, 38, 67, 58, 103, 90, 161, 72, 127, 117, 110, 209, 206, 16,
+ 45, 21, 39, 69, 64, 114, 99, 87, 158, 140, 252, 212, 199, 387, 365, 26,
+ 75, 36, 68, 65, 115, 101, 179, 164, 155, 264, 246, 226, 395, 382, 362, 9,
+ 66, 30, 59, 56, 102, 185, 173, 265, 142, 253, 232, 400, 388, 378, 445, 16,
+ 111, 54, 52, 100, 184, 178, 160, 133, 257, 244, 228, 217, 385, 366, 715, 10,
+ 98, 48, 91, 88, 165, 157, 148, 261, 248, 407, 397, 372, 380, 889, 884, 8,
+ 85, 84, 81, 159, 156, 143, 260, 249, 427, 401, 392, 383, 727, 713, 708, 7,
+ 154, 76, 73, 141, 131, 256, 245, 426, 406, 394, 384, 735, 359, 710, 352, 11,
+ 139, 129, 67, 125, 247, 233, 229, 219, 393, 743, 737, 720, 885, 882, 439, 4,
+ 243, 120, 118, 115, 227, 223, 396, 746, 742, 736, 721, 712, 706, 223, 436, 6,
+ 202, 224, 222, 218, 216, 389, 386, 381, 364, 888, 443, 707, 440, 437, 1728, 4,
+ 747, 211, 210, 208, 370, 379, 734, 723, 714, 1735, 883, 877, 876, 3459, 865, 2,
+ 377, 369, 102, 187, 726, 722, 358, 711, 709, 866, 1734, 871, 3458, 870, 434, 0,
+ 12, 10, 7, 11, 10, 17, 11, 9, 13, 12, 10, 7, 5, 3, 1, 3
+};
+
+static const uint16_t t24HB[] = {
+ 15, 13, 46, 80, 146, 262, 248, 434, 426, 669, 653, 649, 621, 517, 1032, 88,
+ 14, 12, 21, 38, 71, 130, 122, 216, 209, 198, 327, 345, 319, 297, 279, 42,
+ 47, 22, 41, 74, 68, 128, 120, 221, 207, 194, 182, 340, 315, 295, 541, 18,
+ 81, 39, 75, 70, 134, 125, 116, 220, 204, 190, 178, 325, 311, 293, 271, 16,
+ 147, 72, 69, 135, 127, 118, 112, 210, 200, 188, 352, 323, 306, 285, 540, 14,
+ 263, 66, 129, 126, 119, 114, 214, 202, 192, 180, 341, 317, 301, 281, 262, 12,
+ 249, 123, 121, 117, 113, 215, 206, 195, 185, 347, 330, 308, 291, 272, 520, 10,
+ 435, 115, 111, 109, 211, 203, 196, 187, 353, 332, 313, 298, 283, 531, 381, 17,
+ 427, 212, 208, 205, 201, 193, 186, 177, 169, 320, 303, 286, 268, 514, 377, 16,
+ 335, 199, 197, 191, 189, 181, 174, 333, 321, 305, 289, 275, 521, 379, 371, 11,
+ 668, 184, 183, 179, 175, 344, 331, 314, 304, 290, 277, 530, 383, 373, 366, 10,
+ 652, 346, 171, 168, 164, 318, 309, 299, 287, 276, 263, 513, 375, 368, 362, 6,
+ 648, 322, 316, 312, 307, 302, 292, 284, 269, 261, 512, 376, 370, 364, 359, 4,
+ 620, 300, 296, 294, 288, 282, 273, 266, 515, 380, 374, 369, 365, 361, 357, 2,
+ 1033, 280, 278, 274, 267, 264, 259, 382, 378, 372, 367, 363, 360, 358, 356, 0,
+ 43, 20, 19, 17, 15, 13, 11, 9, 7, 6, 4, 7, 5, 3, 1, 3
+};
+
+static const uint16_t t32HB[] = {
+ 1 << 0, 5 << 1, 4 << 1, 5 << 2, 6 << 1, 5 << 2, 4 << 2, 4 << 3,
+ 7 << 1, 3 << 2, 6 << 2, 0 << 3, 7 << 2, 2 << 3, 3 << 3, 1 << 4
+};
+
+static const uint16_t t33HB[] = {
+ 15 << 0, 14 << 1, 13 << 1, 12 << 2, 11 << 1, 10 << 2, 9 << 2, 8 << 3,
+ 7 << 1, 6 << 2, 5 << 2, 4 << 3, 3 << 2, 2 << 3, 1 << 3, 0 << 4
+};
+
+
+const uint8_t t1l[] = {
+ 1, 4,
+ 3, 5
+};
+
+static const uint8_t t2l[] = {
+ 1, 4, 7,
+ 4, 5, 7,
+ 6, 7, 8
+};
+
+static const uint8_t t3l[] = {
+ 2, 3, 7,
+ 4, 4, 7,
+ 6, 7, 8
+};
+
+static const uint8_t t5l[] = {
+ 1, 4, 7, 8,
+ 4, 5, 8, 9,
+ 7, 8, 9, 10,
+ 8, 8, 9, 10
+};
+
+static const uint8_t t6l[] = {
+ 3, 4, 6, 8,
+ 4, 4, 6, 7,
+ 5, 6, 7, 8,
+ 7, 7, 8, 9
+};
+
+static const uint8_t t7l[] = {
+ 1, 4, 7, 9, 9, 10,
+ 4, 6, 8, 9, 9, 10,
+ 7, 7, 9, 10, 10, 11,
+ 8, 9, 10, 11, 11, 11,
+ 8, 9, 10, 11, 11, 12,
+ 9, 10, 11, 12, 12, 12
+};
+
+static const uint8_t t8l[] = {
+ 2, 4, 7, 9, 9, 10,
+ 4, 4, 6, 10, 10, 10,
+ 7, 6, 8, 10, 10, 11,
+ 9, 10, 10, 11, 11, 12,
+ 9, 9, 10, 11, 12, 12,
+ 10, 10, 11, 11, 13, 13
+};
+
+static const uint8_t t9l[] = {
+ 3, 4, 6, 7, 9, 10,
+ 4, 5, 6, 7, 8, 10,
+ 5, 6, 7, 8, 9, 10,
+ 7, 7, 8, 9, 9, 10,
+ 8, 8, 9, 9, 10, 11,
+ 9, 9, 10, 10, 11, 11
+};
+
+static const uint8_t t10l[] = {
+ 1, 4, 7, 9, 10, 10, 10, 11,
+ 4, 6, 8, 9, 10, 11, 10, 10,
+ 7, 8, 9, 10, 11, 12, 11, 11,
+ 8, 9, 10, 11, 12, 12, 11, 12,
+ 9, 10, 11, 12, 12, 12, 12, 12,
+ 10, 11, 12, 12, 13, 13, 12, 13,
+ 9, 10, 11, 12, 12, 12, 13, 13,
+ 10, 10, 11, 12, 12, 13, 13, 13
+};
+
+static const uint8_t t11l[] = {
+ 2, 4, 6, 8, 9, 10, 9, 10,
+ 4, 5, 6, 8, 10, 10, 9, 10,
+ 6, 7, 8, 9, 10, 11, 10, 10,
+ 8, 8, 9, 11, 10, 12, 10, 11,
+ 9, 10, 10, 11, 11, 12, 11, 12,
+ 9, 10, 11, 12, 12, 13, 12, 13,
+ 9, 9, 9, 10, 11, 12, 12, 12,
+ 9, 9, 10, 11, 12, 12, 12, 12
+};
+
+static const uint8_t t12l[] = {
+ 4, 4, 6, 8, 9, 10, 10, 10,
+ 4, 5, 6, 7, 9, 9, 10, 10,
+ 6, 6, 7, 8, 9, 10, 9, 10,
+ 7, 7, 8, 8, 9, 10, 10, 10,
+ 8, 8, 9, 9, 10, 10, 10, 11,
+ 9, 9, 10, 10, 10, 11, 10, 11,
+ 9, 9, 9, 10, 10, 11, 11, 12,
+ 10, 10, 10, 11, 11, 11, 11, 12
+};
+
+static const uint8_t t13l[] = {
+ 1, 5, 7, 8, 9, 10, 10, 11, 10, 11, 12, 12, 13, 13, 14, 14,
+ 4, 6, 8, 9, 10, 10, 11, 11, 11, 11, 12, 12, 13, 14, 14, 14,
+ 7, 8, 9, 10, 11, 11, 12, 12, 11, 12, 12, 13, 13, 14, 15, 15,
+ 8, 9, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 15, 15,
+ 9, 9, 11, 11, 12, 12, 13, 13, 12, 13, 13, 14, 14, 15, 15, 16,
+ 10, 10, 11, 12, 12, 12, 13, 13, 13, 13, 14, 13, 15, 15, 16, 16,
+ 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16,
+ 11, 11, 12, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 16, 18, 18,
+ 10, 10, 11, 12, 12, 13, 13, 14, 14, 14, 14, 15, 15, 16, 17, 17,
+ 11, 11, 12, 12, 13, 13, 13, 15, 14, 15, 15, 16, 16, 16, 18, 17,
+ 11, 12, 12, 13, 13, 14, 14, 15, 14, 15, 16, 15, 16, 17, 18, 19,
+ 12, 12, 12, 13, 14, 14, 14, 14, 15, 15, 15, 16, 17, 17, 17, 18,
+ 12, 13, 13, 14, 14, 15, 14, 15, 16, 16, 17, 17, 17, 18, 18, 18,
+ 13, 13, 14, 15, 15, 15, 16, 16, 16, 16, 16, 17, 18, 17, 18, 18,
+ 14, 14, 14, 15, 15, 15, 17, 16, 16, 19, 17, 17, 17, 19, 18, 18,
+ 13, 14, 15, 16, 16, 16, 17, 16, 17, 17, 18, 18, 21, 20, 21, 18
+};
+
+static const uint8_t t15l[] = {
+ 3, 5, 6, 8, 8, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 14,
+ 5, 5, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13,
+ 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13,
+ 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13,
+ 8, 8, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13,
+ 9, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 13, 13, 13, 14,
+ 10, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14, 14,
+ 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 14,
+ 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14,
+ 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14,
+ 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 15, 14,
+ 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15,
+ 12, 12, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, 14, 15, 15,
+ 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15,
+ 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 14, 15,
+ 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15
+};
+
+static const uint8_t t16_5l[] = {
+ 1, 5, 7, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 11,
+ 4, 6, 8, 9, 10, 11, 11, 11, 12, 12, 12, 13, 14, 13, 14, 11,
+ 7, 8, 9, 10, 11, 11, 12, 12, 13, 12, 13, 13, 13, 14, 14, 12,
+ 9, 9, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 13,
+ 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 12,
+ 10, 10, 11, 11, 12, 13, 13, 14, 13, 14, 14, 15, 15, 15, 16, 13,
+ 11, 11, 11, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 13,
+ 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 17, 17, 13,
+ 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 13,
+ 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 15, 16, 15, 14,
+ 12, 13, 12, 13, 14, 14, 14, 14, 15, 16, 16, 16, 17, 17, 16, 13,
+ 13, 13, 13, 13, 14, 14, 15, 16, 16, 16, 16, 16, 16, 15, 16, 14,
+ 13, 14, 14, 14, 14, 15, 15, 15, 15, 17, 16, 16, 16, 16, 18, 14,
+ 15, 14, 14, 14, 15, 15, 16, 16, 16, 18, 17, 17, 17, 19, 17, 14,
+ 14, 15, 13, 14, 16, 16, 15, 16, 16, 17, 18, 17, 19, 17, 16, 14,
+ 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 12
+};
+
+static const uint8_t t16l[] = {
+ 1, 5, 7, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 10,
+ 4, 6, 8, 9, 10, 11, 11, 11, 12, 12, 12, 13, 14, 13, 14, 10,
+ 7, 8, 9, 10, 11, 11, 12, 12, 13, 12, 13, 13, 13, 14, 14, 11,
+ 9, 9, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 12,
+ 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 11,
+ 10, 10, 11, 11, 12, 13, 13, 14, 13, 14, 14, 15, 15, 15, 16, 12,
+ 11, 11, 11, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 12,
+ 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 17, 17, 12,
+ 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 12,
+ 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 15, 16, 15, 13,
+ 12, 13, 12, 13, 14, 14, 14, 14, 15, 16, 16, 16, 17, 17, 16, 12,
+ 13, 13, 13, 13, 14, 14, 15, 16, 16, 16, 16, 16, 16, 15, 16, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15, 15, 17, 16, 16, 16, 16, 18, 13,
+ 15, 14, 14, 14, 15, 15, 16, 16, 16, 18, 17, 17, 17, 19, 17, 13,
+ 14, 15, 13, 14, 16, 16, 15, 16, 16, 17, 18, 17, 19, 17, 16, 13,
+ 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 10
+};
+
+static const uint8_t t24l[] = {
+ 4, 5, 7, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 13, 10,
+ 5, 6, 7, 8, 9, 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 10,
+ 7, 7, 8, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 9,
+ 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 9,
+ 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, 13, 9,
+ 10, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 9,
+ 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 9,
+ 11, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 10,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 10,
+ 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 10,
+ 12, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 10,
+ 12, 12, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 10,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 10,
+ 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 10,
+ 13, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 10,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 6
+};
+
+const uint8_t t32l[] = {
+ 1 + 0, 4 + 1, 4 + 1, 5 + 2, 4 + 1, 6 + 2, 5 + 2, 6 + 3,
+ 4 + 1, 5 + 2, 5 + 2, 6 + 3, 5 + 2, 6 + 3, 6 + 3, 6 + 4
+};
+
+const uint8_t t33l[] = {
+ 4 + 0, 4 + 1, 4 + 1, 4 + 2, 4 + 1, 4 + 2, 4 + 2, 4 + 3,
+ 4 + 1, 4 + 2, 4 + 2, 4 + 3, 4 + 2, 4 + 3, 4 + 3, 4 + 4
+};
+
+
+const struct huffcodetab ht[HTN] = {
+ /* xlen, linmax, table, hlen */
+ {0, 0, NULL, NULL},
+ {2, 0, t1HB, t1l},
+ {3, 0, t2HB, t2l},
+ {3, 0, t3HB, t3l},
+ {0, 0, NULL, NULL}, /* Apparently not used */
+ {4, 0, t5HB, t5l},
+ {4, 0, t6HB, t6l},
+ {6, 0, t7HB, t7l},
+ {6, 0, t8HB, t8l},
+ {6, 0, t9HB, t9l},
+ {8, 0, t10HB, t10l},
+ {8, 0, t11HB, t11l},
+ {8, 0, t12HB, t12l},
+ {16, 0, t13HB, t13l},
+ {0, 0, NULL, t16_5l}, /* Apparently not used */
+ {16, 0, t15HB, t15l},
+
+ {1, 1, t16HB, t16l},
+ {2, 3, t16HB, t16l},
+ {3, 7, t16HB, t16l},
+ {4, 15, t16HB, t16l},
+ {6, 63, t16HB, t16l},
+ {8, 255, t16HB, t16l},
+ {10, 1023, t16HB, t16l},
+ {13, 8191, t16HB, t16l},
+
+ {4, 15, t24HB, t24l},
+ {5, 31, t24HB, t24l},
+ {6, 63, t24HB, t24l},
+ {7, 127, t24HB, t24l},
+ {8, 255, t24HB, t24l},
+ {9, 511, t24HB, t24l},
+ {11, 2047, t24HB, t24l},
+ {13, 8191, t24HB, t24l},
+
+ {0, 0, t32HB, t32l},
+ {0, 0, t33HB, t33l},
+};
+
+
+
+
+
+/* for (i = 0; i < 16*16; i++) {
+ * largetbl[i] = ((ht[16].hlen[i]) << 16) + ht[24].hlen[i];
+ * }
+ */
+const uint32_t largetbl[16 * 16] = {
+ 0x010004, 0x050005, 0x070007, 0x090008, 0x0a0009, 0x0a000a, 0x0b000a, 0x0b000b,
+ 0x0c000b, 0x0c000c, 0x0c000c, 0x0d000c, 0x0d000c, 0x0d000c, 0x0e000d, 0x0a000a,
+ 0x040005, 0x060006, 0x080007, 0x090008, 0x0a0009, 0x0b000a, 0x0b000a, 0x0b000b,
+ 0x0c000b, 0x0c000b, 0x0c000c, 0x0d000c, 0x0e000c, 0x0d000c, 0x0e000c, 0x0a000a,
+ 0x070007, 0x080007, 0x090008, 0x0a0009, 0x0b0009, 0x0b000a, 0x0c000a, 0x0c000b,
+ 0x0d000b, 0x0c000b, 0x0d000b, 0x0d000c, 0x0d000c, 0x0e000c, 0x0e000d, 0x0b0009,
+ 0x090008, 0x090008, 0x0a0009, 0x0b0009, 0x0b000a, 0x0c000a, 0x0c000a, 0x0c000b,
+ 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x0c0009,
+ 0x0a0009, 0x0a0009, 0x0b0009, 0x0b000a, 0x0c000a, 0x0c000a, 0x0d000a, 0x0d000b,
+ 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000d, 0x0b0009,
+ 0x0a000a, 0x0a0009, 0x0b000a, 0x0b000a, 0x0c000a, 0x0d000a, 0x0d000b, 0x0e000b,
+ 0x0d000b, 0x0e000b, 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x10000c, 0x0c0009,
+ 0x0b000a, 0x0b000a, 0x0b000a, 0x0c000a, 0x0d000a, 0x0d000b, 0x0d000b, 0x0d000b,
+ 0x0e000b, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x10000d, 0x0c0009,
+ 0x0b000b, 0x0b000a, 0x0c000a, 0x0c000a, 0x0d000b, 0x0d000b, 0x0d000b, 0x0e000b,
+ 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x0f000c, 0x11000d, 0x11000d, 0x0c000a,
+ 0x0b000b, 0x0c000b, 0x0c000b, 0x0d000b, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000b,
+ 0x0f000b, 0x0f000c, 0x0f000c, 0x0f000c, 0x10000c, 0x10000d, 0x10000d, 0x0c000a,
+ 0x0c000b, 0x0c000b, 0x0c000b, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000b, 0x0f000c,
+ 0x0f000c, 0x0f000c, 0x0f000c, 0x10000c, 0x0f000d, 0x10000d, 0x0f000d, 0x0d000a,
+ 0x0c000c, 0x0d000b, 0x0c000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0e000c, 0x0e000c,
+ 0x0f000c, 0x10000c, 0x10000c, 0x10000d, 0x11000d, 0x11000d, 0x10000d, 0x0c000a,
+ 0x0d000c, 0x0d000c, 0x0d000b, 0x0d000b, 0x0e000b, 0x0e000c, 0x0f000c, 0x10000c,
+ 0x10000c, 0x10000c, 0x10000c, 0x10000d, 0x10000d, 0x0f000d, 0x10000d, 0x0d000a,
+ 0x0d000c, 0x0e000c, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x0f000c,
+ 0x0f000c, 0x11000c, 0x10000d, 0x10000d, 0x10000d, 0x10000d, 0x12000d, 0x0d000a,
+ 0x0f000c, 0x0e000c, 0x0e000c, 0x0e000c, 0x0f000c, 0x0f000c, 0x10000c, 0x10000c,
+ 0x10000d, 0x12000d, 0x11000d, 0x11000d, 0x11000d, 0x13000d, 0x11000d, 0x0d000a,
+ 0x0e000d, 0x0f000c, 0x0d000c, 0x0e000c, 0x10000c, 0x10000c, 0x0f000c, 0x10000d,
+ 0x10000d, 0x11000d, 0x12000d, 0x11000d, 0x13000d, 0x11000d, 0x10000d, 0x0d000a,
+ 0x0a0009, 0x0a0009, 0x0a0009, 0x0b0009, 0x0b0009, 0x0c0009, 0x0c0009, 0x0c0009,
+ 0x0d0009, 0x0d0009, 0x0d0009, 0x0d000a, 0x0d000a, 0x0d000a, 0x0d000a, 0x0a0006
+};
+
+/* for (i = 0; i < 3*3; i++) {
+ * table23[i] = ((ht[2].hlen[i]) << 16) + ht[3].hlen[i];
+ * }
+ */
+const uint32_t table23[3 * 3] = {
+ 0x010002, 0x040003, 0x070007,
+ 0x040004, 0x050004, 0x070007,
+ 0x060006, 0x070007, 0x080008
+};
+
+/* for (i = 0; i < 4*4; i++) {
+ * table56[i] = ((ht[5].hlen[i]) << 16) + ht[6].hlen[i];
+ * }
+ */
+const uint32_t table56[4 * 4] = {
+ 0x010003, 0x040004, 0x070006, 0x080008, 0x040004, 0x050004, 0x080006, 0x090007,
+ 0x070005, 0x080006, 0x090007, 0x0a0008, 0x080007, 0x080007, 0x090008, 0x0a0009
+};
+
+
+
+/*
+ * 0: MPEG-2 LSF
+ * 1: MPEG-1
+ * 2: MPEG-2.5 LSF FhG extention (1995-07-11 shn)
+ */
+
+typedef enum {
+ MPEG_2 = 0,
+ MPEG_1 = 1,
+ MPEG_25 = 2
+} MPEG_t;
+
+const int bitrate_table[3][16] = {
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}, /* MPEG 2 */
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1}, /* MPEG 1 */
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1, -1}, /* MPEG 2.5 */
+};
+
+const int samplerate_table[3][4] = {
+ {22050, 24000, 16000, -1}, /* MPEG 2 */
+ {44100, 48000, 32000, -1}, /* MPEG 1 */
+ {11025, 12000, 8000, -1}, /* MPEG 2.5 */
+};
+
+int
+lame_get_bitrate(int mpeg_version, int table_index)
+{
+ if (0 <= mpeg_version && mpeg_version <= 2) {
+ if (0 <= table_index && table_index <= 15) {
+ return bitrate_table[mpeg_version][table_index];
+ }
+ }
+ return -1;
+}
+
+int
+lame_get_samplerate(int mpeg_version, int table_index)
+{
+ if (0 <= mpeg_version && mpeg_version <= 2) {
+ if (0 <= table_index && table_index <= 3) {
+ return samplerate_table[mpeg_version][table_index];
+ }
+ }
+ return -1;
+}
+
+
+/* This is the scfsi_band table from 2.4.2.7 of the IS */
+const int scfsi_band[5] = { 0, 6, 11, 16, 21 };
+
+/* end of tables.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/tables.h b/libnative/src/main/cpp/module/mp3/lame/tables.h
new file mode 100644
index 0000000000000000000000000000000000000000..0dd7deb20767e0a8bd63531e01eea26b35cacb93
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/tables.h
@@ -0,0 +1,95 @@
+/*
+ * MPEG layer 3 tables include file
+ *
+ * Copyright (c) 1999 Albert L Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_TABLES_H
+#define LAME_TABLES_H
+
+#if 0
+typedef struct {
+ unsigned char no;
+ unsigned char width;
+ unsigned char minval_2;
+ float quiet_thr;
+ float norm;
+ float bark;
+} type1_t;
+
+typedef struct {
+ unsigned char no;
+ unsigned char width;
+ float quiet_thr;
+ float norm;
+ float SNR;
+ float bark;
+} type2_t;
+
+typedef struct {
+ unsigned int no:5;
+ unsigned int cbw:3;
+ unsigned int bu:6;
+ unsigned int bo:6;
+ unsigned int w1_576:10;
+ unsigned int w2_576:10;
+} type34_t;
+
+typedef struct {
+ size_t len1;
+ const type1_t *const tab1;
+ size_t len2;
+ const type2_t *const tab2;
+ size_t len3;
+ const type34_t *const tab3;
+ size_t len4;
+ const type34_t *const tab4;
+} type5_t;
+
+extern const type5_t table5[6];
+
+#endif
+
+#define HTN 34
+
+struct huffcodetab {
+ const unsigned int xlen; /* max. x-index+ */
+ const unsigned int linmax; /* max number to be stored in linbits */
+ const uint16_t *table; /* pointer to array[xlen][ylen] */
+ const uint8_t *hlen; /* pointer to array[xlen][ylen] */
+};
+
+extern const struct huffcodetab ht[HTN];
+ /* global memory block */
+ /* array of all huffcodtable headers */
+ /* 0..31 Huffman code table 0..31 */
+ /* 32,33 count1-tables */
+
+extern const uint8_t t32l[];
+extern const uint8_t t33l[];
+
+extern const uint32_t largetbl[16 * 16];
+extern const uint32_t table23[3 * 3];
+extern const uint32_t table56[4 * 4];
+
+extern const int scfsi_band[5];
+
+extern const int bitrate_table [3][16];
+extern const int samplerate_table [3][ 4];
+
+#endif /* LAME_TABLES_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/takehiro.c b/libnative/src/main/cpp/module/mp3/lame/takehiro.c
new file mode 100644
index 0000000000000000000000000000000000000000..9781feb22beb1e6407ff05accd22632d3424f7c1
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/takehiro.c
@@ -0,0 +1,1375 @@
+/*
+ * MP3 huffman table selecting and bit counting
+ *
+ * Copyright (c) 1999-2005 Takehiro TOMINAGA
+ * Copyright (c) 2002-2005 Gabriel Bouvigne
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: takehiro.c,v 1.79 2011/05/07 16:05:17 rbrito Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "quantize_pvt.h"
+#include "tables.h"
+
+
+static const struct {
+ const int region0_count;
+ const int region1_count;
+} subdv_table[23] = {
+ {
+ 0, 0}, /* 0 bands */
+ {
+ 0, 0}, /* 1 bands */
+ {
+ 0, 0}, /* 2 bands */
+ {
+ 0, 0}, /* 3 bands */
+ {
+ 0, 0}, /* 4 bands */
+ {
+ 0, 1}, /* 5 bands */
+ {
+ 1, 1}, /* 6 bands */
+ {
+ 1, 1}, /* 7 bands */
+ {
+ 1, 2}, /* 8 bands */
+ {
+ 2, 2}, /* 9 bands */
+ {
+ 2, 3}, /* 10 bands */
+ {
+ 2, 3}, /* 11 bands */
+ {
+ 3, 4}, /* 12 bands */
+ {
+ 3, 4}, /* 13 bands */
+ {
+ 3, 4}, /* 14 bands */
+ {
+ 4, 5}, /* 15 bands */
+ {
+ 4, 5}, /* 16 bands */
+ {
+ 4, 6}, /* 17 bands */
+ {
+ 5, 6}, /* 18 bands */
+ {
+ 5, 6}, /* 19 bands */
+ {
+ 5, 7}, /* 20 bands */
+ {
+ 6, 7}, /* 21 bands */
+ {
+ 6, 7}, /* 22 bands */
+};
+
+
+
+
+
+/*********************************************************************
+ * nonlinear quantization of xr
+ * More accurate formula than the ISO formula. Takes into account
+ * the fact that we are quantizing xr -> ix, but we want ix^4/3 to be
+ * as close as possible to x^4/3. (taking the nearest int would mean
+ * ix is as close as possible to xr, which is different.)
+ *
+ * From Segher Boessenkool 11/1999
+ *
+ * 09/2000: ASM code removed in favor of IEEE754 hack by Takehiro
+ * Tominaga. If you need the ASM code, check CVS circa Aug 2000.
+ *
+ * 01/2004: Optimizations by Gabriel Bouvigne
+ *********************************************************************/
+
+
+
+
+
+static void
+quantize_lines_xrpow_01(unsigned int l, FLOAT istep, const FLOAT * xr, int *ix)
+{
+ const FLOAT compareval0 = (1.0f - 0.4054f) / istep;
+ unsigned int i;
+
+ assert(l > 0);
+ assert(l % 2 == 0);
+ for (i = 0; i < l; i += 2) {
+ FLOAT const xr_0 = xr[i+0];
+ FLOAT const xr_1 = xr[i+1];
+ int const ix_0 = (compareval0 > xr_0) ? 0 : 1;
+ int const ix_1 = (compareval0 > xr_1) ? 0 : 1;
+ ix[i+0] = ix_0;
+ ix[i+1] = ix_1;
+ }
+}
+
+
+
+#ifdef TAKEHIRO_IEEE754_HACK
+
+typedef union {
+ float f;
+ int i;
+} fi_union;
+
+#define MAGIC_FLOAT (65536*(128))
+#define MAGIC_INT 0x4b000000
+
+
+static void
+quantize_lines_xrpow(unsigned int l, FLOAT istep, const FLOAT * xp, int *pi)
+{
+ fi_union *fi;
+ unsigned int remaining;
+
+ assert(l > 0);
+
+ fi = (fi_union *) pi;
+
+ l = l >> 1;
+ remaining = l % 2;
+ l = l >> 1;
+ while (l--) {
+ double x0 = istep * xp[0];
+ double x1 = istep * xp[1];
+ double x2 = istep * xp[2];
+ double x3 = istep * xp[3];
+
+ x0 += MAGIC_FLOAT;
+ fi[0].f = x0;
+ x1 += MAGIC_FLOAT;
+ fi[1].f = x1;
+ x2 += MAGIC_FLOAT;
+ fi[2].f = x2;
+ x3 += MAGIC_FLOAT;
+ fi[3].f = x3;
+
+ fi[0].f = x0 + adj43asm[fi[0].i - MAGIC_INT];
+ fi[1].f = x1 + adj43asm[fi[1].i - MAGIC_INT];
+ fi[2].f = x2 + adj43asm[fi[2].i - MAGIC_INT];
+ fi[3].f = x3 + adj43asm[fi[3].i - MAGIC_INT];
+
+ fi[0].i -= MAGIC_INT;
+ fi[1].i -= MAGIC_INT;
+ fi[2].i -= MAGIC_INT;
+ fi[3].i -= MAGIC_INT;
+ fi += 4;
+ xp += 4;
+ };
+ if (remaining) {
+ double x0 = istep * xp[0];
+ double x1 = istep * xp[1];
+
+ x0 += MAGIC_FLOAT;
+ fi[0].f = x0;
+ x1 += MAGIC_FLOAT;
+ fi[1].f = x1;
+
+ fi[0].f = x0 + adj43asm[fi[0].i - MAGIC_INT];
+ fi[1].f = x1 + adj43asm[fi[1].i - MAGIC_INT];
+
+ fi[0].i -= MAGIC_INT;
+ fi[1].i -= MAGIC_INT;
+ }
+
+}
+
+
+#else
+
+/*********************************************************************
+ * XRPOW_FTOI is a macro to convert floats to ints.
+ * if XRPOW_FTOI(x) = nearest_int(x), then QUANTFAC(x)=adj43asm[x]
+ * ROUNDFAC= -0.0946
+ *
+ * if XRPOW_FTOI(x) = floor(x), then QUANTFAC(x)=asj43[x]
+ * ROUNDFAC=0.4054
+ *
+ * Note: using floor() or (int) is extremely slow. On machines where
+ * the TAKEHIRO_IEEE754_HACK code above does not work, it is worthwile
+ * to write some ASM for XRPOW_FTOI().
+ *********************************************************************/
+#define XRPOW_FTOI(src,dest) ((dest) = (int)(src))
+#define QUANTFAC(rx) adj43[rx]
+#define ROUNDFAC 0.4054
+
+
+static void
+quantize_lines_xrpow(unsigned int l, FLOAT istep, const FLOAT * xr, int *ix)
+{
+ unsigned int remaining;
+
+ assert(l > 0);
+
+ l = l >> 1;
+ remaining = l % 2;
+ l = l >> 1;
+ while (l--) {
+ FLOAT x0, x1, x2, x3;
+ int rx0, rx1, rx2, rx3;
+
+ x0 = *xr++ * istep;
+ x1 = *xr++ * istep;
+ XRPOW_FTOI(x0, rx0);
+ x2 = *xr++ * istep;
+ XRPOW_FTOI(x1, rx1);
+ x3 = *xr++ * istep;
+ XRPOW_FTOI(x2, rx2);
+ x0 += QUANTFAC(rx0);
+ XRPOW_FTOI(x3, rx3);
+ x1 += QUANTFAC(rx1);
+ XRPOW_FTOI(x0, *ix++);
+ x2 += QUANTFAC(rx2);
+ XRPOW_FTOI(x1, *ix++);
+ x3 += QUANTFAC(rx3);
+ XRPOW_FTOI(x2, *ix++);
+ XRPOW_FTOI(x3, *ix++);
+ };
+ if (remaining) {
+ FLOAT x0, x1;
+ int rx0, rx1;
+
+ x0 = *xr++ * istep;
+ x1 = *xr++ * istep;
+ XRPOW_FTOI(x0, rx0);
+ XRPOW_FTOI(x1, rx1);
+ x0 += QUANTFAC(rx0);
+ x1 += QUANTFAC(rx1);
+ XRPOW_FTOI(x0, *ix++);
+ XRPOW_FTOI(x1, *ix++);
+ }
+
+}
+
+
+
+#endif
+
+
+
+/*********************************************************************
+ * Quantization function
+ * This function will select which lines to quantize and call the
+ * proper quantization function
+ *********************************************************************/
+
+static void
+quantize_xrpow(const FLOAT * xp, int *pi, FLOAT istep, gr_info const *const cod_info,
+ calc_noise_data const *prev_noise)
+{
+ /* quantize on xr^(3/4) instead of xr */
+ int sfb;
+ int sfbmax;
+ int j = 0;
+ int prev_data_use;
+ int *iData;
+ int accumulate = 0;
+ int accumulate01 = 0;
+ int *acc_iData;
+ const FLOAT *acc_xp;
+
+ iData = pi;
+ acc_xp = xp;
+ acc_iData = iData;
+
+
+ /* Reusing previously computed data does not seems to work if global gain
+ is changed. Finding why it behaves this way would allow to use a cache of
+ previously computed values (let's 10 cached values per sfb) that would
+ probably provide a noticeable speedup */
+ prev_data_use = (prev_noise && (cod_info->global_gain == prev_noise->global_gain));
+
+ if (cod_info->block_type == SHORT_TYPE)
+ sfbmax = 38;
+ else
+ sfbmax = 21;
+
+ for (sfb = 0; sfb <= sfbmax; sfb++) {
+ int step = -1;
+
+ if (prev_data_use || cod_info->block_type == NORM_TYPE) {
+ step =
+ cod_info->global_gain
+ - ((cod_info->scalefac[sfb] + (cod_info->preflag ? pretab[sfb] : 0))
+ << (cod_info->scalefac_scale + 1))
+ - cod_info->subblock_gain[cod_info->window[sfb]] * 8;
+ }
+ assert(cod_info->width[sfb] >= 0);
+ if (prev_data_use && (prev_noise->step[sfb] == step)) {
+ /* do not recompute this part,
+ but compute accumulated lines */
+ if (accumulate) {
+ quantize_lines_xrpow(accumulate, istep, acc_xp, acc_iData);
+ accumulate = 0;
+ }
+ if (accumulate01) {
+ quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_iData);
+ accumulate01 = 0;
+ }
+ }
+ else { /*should compute this part */
+ int l;
+ l = cod_info->width[sfb];
+
+ if ((j + cod_info->width[sfb]) > cod_info->max_nonzero_coeff) {
+ /*do not compute upper zero part */
+ int usefullsize;
+ usefullsize = cod_info->max_nonzero_coeff - j + 1;
+ memset(&pi[cod_info->max_nonzero_coeff], 0,
+ sizeof(int) * (576 - cod_info->max_nonzero_coeff));
+ l = usefullsize;
+
+ if (l < 0) {
+ l = 0;
+ }
+
+ /* no need to compute higher sfb values */
+ sfb = sfbmax + 1;
+ }
+
+ /*accumulate lines to quantize */
+ if (!accumulate && !accumulate01) {
+ acc_iData = iData;
+ acc_xp = xp;
+ }
+ if (prev_noise &&
+ prev_noise->sfb_count1 > 0 &&
+ sfb >= prev_noise->sfb_count1 &&
+ prev_noise->step[sfb] > 0 && step >= prev_noise->step[sfb]) {
+
+ if (accumulate) {
+ quantize_lines_xrpow(accumulate, istep, acc_xp, acc_iData);
+ accumulate = 0;
+ acc_iData = iData;
+ acc_xp = xp;
+ }
+ accumulate01 += l;
+ }
+ else {
+ if (accumulate01) {
+ quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_iData);
+ accumulate01 = 0;
+ acc_iData = iData;
+ acc_xp = xp;
+ }
+ accumulate += l;
+ }
+
+ if (l <= 0) {
+ /* rh: 20040215
+ * may happen due to "prev_data_use" optimization
+ */
+ if (accumulate01) {
+ quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_iData);
+ accumulate01 = 0;
+ }
+ if (accumulate) {
+ quantize_lines_xrpow(accumulate, istep, acc_xp, acc_iData);
+ accumulate = 0;
+ }
+
+ break; /* ends for-loop */
+ }
+ }
+ if (sfb <= sfbmax) {
+ iData += cod_info->width[sfb];
+ xp += cod_info->width[sfb];
+ j += cod_info->width[sfb];
+ }
+ }
+ if (accumulate) { /*last data part */
+ quantize_lines_xrpow(accumulate, istep, acc_xp, acc_iData);
+ accumulate = 0;
+ }
+ if (accumulate01) { /*last data part */
+ quantize_lines_xrpow_01(accumulate01, istep, acc_xp, acc_iData);
+ accumulate01 = 0;
+ }
+
+}
+
+
+
+
+/*************************************************************************/
+/* ix_max */
+/*************************************************************************/
+
+static int
+ix_max(const int *ix, const int *end)
+{
+ int max1 = 0, max2 = 0;
+
+ do {
+ int const x1 = *ix++;
+ int const x2 = *ix++;
+ if (max1 < x1)
+ max1 = x1;
+
+ if (max2 < x2)
+ max2 = x2;
+ } while (ix < end);
+ if (max1 < max2)
+ max1 = max2;
+ return max1;
+}
+
+
+
+
+
+
+
+
+static int
+count_bit_ESC(const int *ix, const int *const end, int t1, const int t2, unsigned int *const s)
+{
+ /* ESC-table is used */
+ unsigned int const linbits = ht[t1].xlen * 65536u + ht[t2].xlen;
+ unsigned int sum = 0, sum2;
+
+ do {
+ unsigned int x = *ix++;
+ unsigned int y = *ix++;
+
+ if (x >= 15u) {
+ x = 15u;
+ sum += linbits;
+ }
+ if (y >= 15u) {
+ y = 15u;
+ sum += linbits;
+ }
+ x <<= 4u;
+ x += y;
+ sum += largetbl[x];
+ } while (ix < end);
+
+ sum2 = sum & 0xffffu;
+ sum >>= 16u;
+
+ if (sum > sum2) {
+ sum = sum2;
+ t1 = t2;
+ }
+
+ *s += sum;
+ return t1;
+}
+
+
+static int
+count_bit_noESC(const int *ix, const int *end, int mx, unsigned int *s)
+{
+ /* No ESC-words */
+ unsigned int sum1 = 0;
+ const uint8_t *const hlen1 = ht[1].hlen;
+ (void) mx;
+
+ do {
+ unsigned int const x0 = *ix++;
+ unsigned int const x1 = *ix++;
+ sum1 += hlen1[ x0+x0 + x1 ];
+ } while (ix < end);
+
+ *s += sum1;
+ return 1;
+}
+
+
+static const int huf_tbl_noESC[] = {
+ 1, 2, 5, 7, 7, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13
+};
+
+
+static int
+count_bit_noESC_from2(const int *ix, const int *end, int max, unsigned int *s)
+{
+ int t1 = huf_tbl_noESC[max - 1];
+ /* No ESC-words */
+ const unsigned int xlen = ht[t1].xlen;
+ uint32_t const* table = (t1 == 2) ? &table23[0] : &table56[0];
+ unsigned int sum = 0, sum2;
+
+ do {
+ unsigned int const x0 = *ix++;
+ unsigned int const x1 = *ix++;
+ sum += table[ x0 * xlen + x1 ];
+ } while (ix < end);
+
+ sum2 = sum & 0xffffu;
+ sum >>= 16u;
+
+ if (sum > sum2) {
+ sum = sum2;
+ t1++;
+ }
+
+ *s += sum;
+ return t1;
+}
+
+
+inline static int
+count_bit_noESC_from3(const int *ix, const int *end, int max, unsigned int * s)
+{
+ int t1 = huf_tbl_noESC[max - 1];
+ /* No ESC-words */
+ unsigned int sum1 = 0;
+ unsigned int sum2 = 0;
+ unsigned int sum3 = 0;
+ const unsigned int xlen = ht[t1].xlen;
+ const uint8_t *const hlen1 = ht[t1].hlen;
+ const uint8_t *const hlen2 = ht[t1 + 1].hlen;
+ const uint8_t *const hlen3 = ht[t1 + 2].hlen;
+ int t;
+
+ do {
+ unsigned int x0 = *ix++;
+ unsigned int x1 = *ix++;
+ unsigned int x = x0 * xlen + x1;
+ sum1 += hlen1[x];
+ sum2 += hlen2[x];
+ sum3 += hlen3[x];
+ } while (ix < end);
+
+ t = t1;
+ if (sum1 > sum2) {
+ sum1 = sum2;
+ t++;
+ }
+ if (sum1 > sum3) {
+ sum1 = sum3;
+ t = t1 + 2;
+ }
+ *s += sum1;
+
+ return t;
+}
+
+
+/*************************************************************************/
+/* choose table */
+/*************************************************************************/
+
+/*
+ Choose the Huffman table that will encode ix[begin..end] with
+ the fewest bits.
+
+ Note: This code contains knowledge about the sizes and characteristics
+ of the Huffman tables as defined in the IS (Table B.7), and will not work
+ with any arbitrary tables.
+*/
+static int count_bit_null(const int* ix, const int* end, int max, unsigned int* s)
+{
+ (void) ix;
+ (void) end;
+ (void) max;
+ (void) s;
+ return 0;
+}
+
+typedef int (*count_fnc)(const int* ix, const int* end, int max, unsigned int* s);
+
+static count_fnc count_fncs[] =
+{ &count_bit_null
+, &count_bit_noESC
+, &count_bit_noESC_from2
+, &count_bit_noESC_from2
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+, &count_bit_noESC_from3
+};
+
+static int
+choose_table_nonMMX(const int *ix, const int *const end, int *const _s)
+{
+ unsigned int* s = (unsigned int*)_s;
+ unsigned int max;
+ int choice, choice2;
+ max = ix_max(ix, end);
+
+ if (max <= 15) {
+ return count_fncs[max](ix, end, max, s);
+ }
+ /* try tables with linbits */
+ if (max > IXMAX_VAL) {
+ *s = LARGE_BITS;
+ return -1;
+ }
+ max -= 15u;
+ for (choice2 = 24; choice2 < 32; choice2++) {
+ if (ht[choice2].linmax >= max) {
+ break;
+ }
+ }
+
+ for (choice = choice2 - 8; choice < 24; choice++) {
+ if (ht[choice].linmax >= max) {
+ break;
+ }
+ }
+ return count_bit_ESC(ix, end, choice, choice2, s);
+}
+
+
+
+/*************************************************************************/
+/* count_bit */
+/*************************************************************************/
+int
+noquant_count_bits(lame_internal_flags const *const gfc,
+ gr_info * const gi, calc_noise_data * prev_noise)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int bits = 0;
+ int i, a1, a2;
+ int const *const ix = gi->l3_enc;
+
+ i = Min(576, ((gi->max_nonzero_coeff + 2) >> 1) << 1);
+
+ if (prev_noise)
+ prev_noise->sfb_count1 = 0;
+
+ /* Determine count1 region */
+ for (; i > 1; i -= 2)
+ if (ix[i - 1] | ix[i - 2])
+ break;
+ gi->count1 = i;
+
+ /* Determines the number of bits to encode the quadruples. */
+ a1 = a2 = 0;
+ for (; i > 3; i -= 4) {
+ int x4 = ix[i-4];
+ int x3 = ix[i-3];
+ int x2 = ix[i-2];
+ int x1 = ix[i-1];
+ int p;
+ /* hack to check if all values <= 1 */
+ if ((unsigned int) (x4 | x3 | x2 | x1) > 1)
+ break;
+
+ p = ((x4 * 2 + x3) * 2 + x2) * 2 + x1;
+ a1 += t32l[p];
+ a2 += t33l[p];
+ }
+
+ bits = a1;
+ gi->count1table_select = 0;
+ if (a1 > a2) {
+ bits = a2;
+ gi->count1table_select = 1;
+ }
+
+ gi->count1bits = bits;
+ gi->big_values = i;
+ if (i == 0)
+ return bits;
+
+ if (gi->block_type == SHORT_TYPE) {
+ a1 = 3 * gfc->scalefac_band.s[3];
+ if (a1 > gi->big_values)
+ a1 = gi->big_values;
+ a2 = gi->big_values;
+
+ }
+ else if (gi->block_type == NORM_TYPE) {
+ assert(i <= 576); /* bv_scf has 576 entries (0..575) */
+ a1 = gi->region0_count = gfc->sv_qnt.bv_scf[i - 2];
+ a2 = gi->region1_count = gfc->sv_qnt.bv_scf[i - 1];
+
+ assert(a1 + a2 + 2 < SBPSY_l);
+ a2 = gfc->scalefac_band.l[a1 + a2 + 2];
+ a1 = gfc->scalefac_band.l[a1 + 1];
+ if (a2 < i)
+ gi->table_select[2] = gfc->choose_table(ix + a2, ix + i, &bits);
+
+ }
+ else {
+ gi->region0_count = 7;
+ /*gi->region1_count = SBPSY_l - 7 - 1; */
+ gi->region1_count = SBMAX_l - 1 - 7 - 1;
+ a1 = gfc->scalefac_band.l[7 + 1];
+ a2 = i;
+ if (a1 > a2) {
+ a1 = a2;
+ }
+ }
+
+
+ /* have to allow for the case when bigvalues < region0 < region1 */
+ /* (and region0, region1 are ignored) */
+ a1 = Min(a1, i);
+ a2 = Min(a2, i);
+
+ assert(a1 >= 0);
+ assert(a2 >= 0);
+
+ /* Count the number of bits necessary to code the bigvalues region. */
+ if (0 < a1)
+ gi->table_select[0] = gfc->choose_table(ix, ix + a1, &bits);
+ if (a1 < a2)
+ gi->table_select[1] = gfc->choose_table(ix + a1, ix + a2, &bits);
+ if (cfg->use_best_huffman == 2) {
+ gi->part2_3_length = bits;
+ best_huffman_divide(gfc, gi);
+ bits = gi->part2_3_length;
+ }
+
+
+ if (prev_noise) {
+ if (gi->block_type == NORM_TYPE) {
+ int sfb = 0;
+ while (gfc->scalefac_band.l[sfb] < gi->big_values) {
+ sfb++;
+ }
+ prev_noise->sfb_count1 = sfb;
+ }
+ }
+
+ return bits;
+}
+
+int
+count_bits(lame_internal_flags const *const gfc,
+ const FLOAT * const xr, gr_info * const gi, calc_noise_data * prev_noise)
+{
+ int *const ix = gi->l3_enc;
+
+ /* since quantize_xrpow uses table lookup, we need to check this first: */
+ FLOAT const w = (IXMAX_VAL) / IPOW20(gi->global_gain);
+
+ if (gi->xrpow_max > w)
+ return LARGE_BITS;
+
+ quantize_xrpow(xr, ix, IPOW20(gi->global_gain), gi, prev_noise);
+
+ if (gfc->sv_qnt.substep_shaping & 2) {
+ int sfb, j = 0;
+ /* 0.634521682242439 = 0.5946*2**(.5*0.1875) */
+ int const gain = gi->global_gain + gi->scalefac_scale;
+ const FLOAT roundfac = 0.634521682242439 / IPOW20(gain);
+ for (sfb = 0; sfb < gi->sfbmax; sfb++) {
+ int const width = gi->width[sfb];
+ assert(width >= 0);
+ if (!gfc->sv_qnt.pseudohalf[sfb]) {
+ j += width;
+ }
+ else {
+ int k;
+ for (k = j, j += width; k < j; ++k) {
+ ix[k] = (xr[k] >= roundfac) ? ix[k] : 0;
+ }
+ }
+ }
+ }
+ return noquant_count_bits(gfc, gi, prev_noise);
+}
+
+/***********************************************************************
+ re-calculate the best scalefac_compress using scfsi
+ the saved bits are kept in the bit reservoir.
+ **********************************************************************/
+
+
+inline static void
+recalc_divide_init(const lame_internal_flags * const gfc,
+ gr_info const *cod_info,
+ int const *const ix, int r01_bits[], int r01_div[], int r0_tbl[], int r1_tbl[])
+{
+ int r0, r1, bigv, r0t, r1t, bits;
+
+ bigv = cod_info->big_values;
+
+ for (r0 = 0; r0 <= 7 + 15; r0++) {
+ r01_bits[r0] = LARGE_BITS;
+ }
+
+ for (r0 = 0; r0 < 16; r0++) {
+ int const a1 = gfc->scalefac_band.l[r0 + 1];
+ int r0bits;
+ if (a1 >= bigv)
+ break;
+ r0bits = 0;
+ r0t = gfc->choose_table(ix, ix + a1, &r0bits);
+
+ for (r1 = 0; r1 < 8; r1++) {
+ int const a2 = gfc->scalefac_band.l[r0 + r1 + 2];
+ if (a2 >= bigv)
+ break;
+
+ bits = r0bits;
+ r1t = gfc->choose_table(ix + a1, ix + a2, &bits);
+ if (r01_bits[r0 + r1] > bits) {
+ r01_bits[r0 + r1] = bits;
+ r01_div[r0 + r1] = r0;
+ r0_tbl[r0 + r1] = r0t;
+ r1_tbl[r0 + r1] = r1t;
+ }
+ }
+ }
+}
+
+inline static void
+recalc_divide_sub(const lame_internal_flags * const gfc,
+ const gr_info * cod_info2,
+ gr_info * const gi,
+ const int *const ix,
+ const int r01_bits[], const int r01_div[], const int r0_tbl[], const int r1_tbl[])
+{
+ int bits, r2, a2, bigv, r2t;
+
+ bigv = cod_info2->big_values;
+
+ for (r2 = 2; r2 < SBMAX_l + 1; r2++) {
+ a2 = gfc->scalefac_band.l[r2];
+ if (a2 >= bigv)
+ break;
+
+ bits = r01_bits[r2 - 2] + cod_info2->count1bits;
+ if (gi->part2_3_length <= bits)
+ break;
+
+ r2t = gfc->choose_table(ix + a2, ix + bigv, &bits);
+ if (gi->part2_3_length <= bits)
+ continue;
+
+ memcpy(gi, cod_info2, sizeof(gr_info));
+ gi->part2_3_length = bits;
+ gi->region0_count = r01_div[r2 - 2];
+ gi->region1_count = r2 - 2 - r01_div[r2 - 2];
+ gi->table_select[0] = r0_tbl[r2 - 2];
+ gi->table_select[1] = r1_tbl[r2 - 2];
+ gi->table_select[2] = r2t;
+ }
+}
+
+
+
+
+void
+best_huffman_divide(const lame_internal_flags * const gfc, gr_info * const gi)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int i, a1, a2;
+ gr_info cod_info2;
+ int const *const ix = gi->l3_enc;
+
+ int r01_bits[7 + 15 + 1];
+ int r01_div[7 + 15 + 1];
+ int r0_tbl[7 + 15 + 1];
+ int r1_tbl[7 + 15 + 1];
+
+
+ /* SHORT BLOCK stuff fails for MPEG2 */
+ if (gi->block_type == SHORT_TYPE && cfg->mode_gr == 1)
+ return;
+
+
+ memcpy(&cod_info2, gi, sizeof(gr_info));
+ if (gi->block_type == NORM_TYPE) {
+ recalc_divide_init(gfc, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
+ recalc_divide_sub(gfc, &cod_info2, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
+ }
+
+ i = cod_info2.big_values;
+ if (i == 0 || (unsigned int) (ix[i - 2] | ix[i - 1]) > 1)
+ return;
+
+ i = gi->count1 + 2;
+ if (i > 576)
+ return;
+
+ /* Determines the number of bits to encode the quadruples. */
+ memcpy(&cod_info2, gi, sizeof(gr_info));
+ cod_info2.count1 = i;
+ a1 = a2 = 0;
+
+ assert(i <= 576);
+
+ for (; i > cod_info2.big_values; i -= 4) {
+ int const p = ((ix[i - 4] * 2 + ix[i - 3]) * 2 + ix[i - 2]) * 2 + ix[i - 1];
+ a1 += t32l[p];
+ a2 += t33l[p];
+ }
+ cod_info2.big_values = i;
+
+ cod_info2.count1table_select = 0;
+ if (a1 > a2) {
+ a1 = a2;
+ cod_info2.count1table_select = 1;
+ }
+
+ cod_info2.count1bits = a1;
+
+ if (cod_info2.block_type == NORM_TYPE)
+ recalc_divide_sub(gfc, &cod_info2, gi, ix, r01_bits, r01_div, r0_tbl, r1_tbl);
+ else {
+ /* Count the number of bits necessary to code the bigvalues region. */
+ cod_info2.part2_3_length = a1;
+ a1 = gfc->scalefac_band.l[7 + 1];
+ if (a1 > i) {
+ a1 = i;
+ }
+ if (a1 > 0)
+ cod_info2.table_select[0] =
+ gfc->choose_table(ix, ix + a1, (int *) &cod_info2.part2_3_length);
+ if (i > a1)
+ cod_info2.table_select[1] =
+ gfc->choose_table(ix + a1, ix + i, (int *) &cod_info2.part2_3_length);
+ if (gi->part2_3_length > cod_info2.part2_3_length)
+ memcpy(gi, &cod_info2, sizeof(gr_info));
+ }
+}
+
+static const int slen1_n[16] = { 1, 1, 1, 1, 8, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16 };
+static const int slen2_n[16] = { 1, 2, 4, 8, 1, 2, 4, 8, 2, 4, 8, 2, 4, 8, 4, 8 };
+const int slen1_tab[16] = { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 };
+const int slen2_tab[16] = { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 };
+
+static void
+scfsi_calc(int ch, III_side_info_t * l3_side)
+{
+ unsigned int i;
+ int s1, s2, c1, c2;
+ int sfb;
+ gr_info *const gi = &l3_side->tt[1][ch];
+ gr_info const *const g0 = &l3_side->tt[0][ch];
+
+ for (i = 0; i < (sizeof(scfsi_band) / sizeof(int)) - 1; i++) {
+ for (sfb = scfsi_band[i]; sfb < scfsi_band[i + 1]; sfb++) {
+ if (g0->scalefac[sfb] != gi->scalefac[sfb]
+ && gi->scalefac[sfb] >= 0)
+ break;
+ }
+ if (sfb == scfsi_band[i + 1]) {
+ for (sfb = scfsi_band[i]; sfb < scfsi_band[i + 1]; sfb++) {
+ gi->scalefac[sfb] = -1;
+ }
+ l3_side->scfsi[ch][i] = 1;
+ }
+ }
+
+ s1 = c1 = 0;
+ for (sfb = 0; sfb < 11; sfb++) {
+ if (gi->scalefac[sfb] == -1)
+ continue;
+ c1++;
+ if (s1 < gi->scalefac[sfb])
+ s1 = gi->scalefac[sfb];
+ }
+
+ s2 = c2 = 0;
+ for (; sfb < SBPSY_l; sfb++) {
+ if (gi->scalefac[sfb] == -1)
+ continue;
+ c2++;
+ if (s2 < gi->scalefac[sfb])
+ s2 = gi->scalefac[sfb];
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (s1 < slen1_n[i] && s2 < slen2_n[i]) {
+ int const c = slen1_tab[i] * c1 + slen2_tab[i] * c2;
+ if (gi->part2_length > c) {
+ gi->part2_length = c;
+ gi->scalefac_compress = (int)i;
+ }
+ }
+ }
+}
+
+/*
+Find the optimal way to store the scalefactors.
+Only call this routine after final scalefactors have been
+chosen and the channel/granule will not be re-encoded.
+ */
+void
+best_scalefac_store(const lame_internal_flags * gfc,
+ const int gr, const int ch, III_side_info_t * const l3_side)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ /* use scalefac_scale if we can */
+ gr_info *const gi = &l3_side->tt[gr][ch];
+ int sfb, i, j, l;
+ int recalc = 0;
+
+ /* remove scalefacs from bands with ix=0. This idea comes
+ * from the AAC ISO docs. added mt 3/00 */
+ /* check if l3_enc=0 */
+ j = 0;
+ for (sfb = 0; sfb < gi->sfbmax; sfb++) {
+ int const width = gi->width[sfb];
+ assert(width >= 0);
+ for (l = j, j += width; l < j; ++l) {
+ if (gi->l3_enc[l] != 0)
+ break;
+ }
+ if (l == j)
+ gi->scalefac[sfb] = recalc = -2; /* anything goes. */
+ /* only best_scalefac_store and calc_scfsi
+ * know--and only they should know--about the magic number -2.
+ */
+ }
+
+ if (!gi->scalefac_scale && !gi->preflag) {
+ int s = 0;
+ for (sfb = 0; sfb < gi->sfbmax; sfb++)
+ if (gi->scalefac[sfb] > 0)
+ s |= gi->scalefac[sfb];
+
+ if (!(s & 1) && s != 0) {
+ for (sfb = 0; sfb < gi->sfbmax; sfb++)
+ if (gi->scalefac[sfb] > 0)
+ gi->scalefac[sfb] >>= 1;
+
+ gi->scalefac_scale = recalc = 1;
+ }
+ }
+
+ if (!gi->preflag && gi->block_type != SHORT_TYPE && cfg->mode_gr == 2) {
+ for (sfb = 11; sfb < SBPSY_l; sfb++)
+ if (gi->scalefac[sfb] < pretab[sfb] && gi->scalefac[sfb] != -2)
+ break;
+ if (sfb == SBPSY_l) {
+ for (sfb = 11; sfb < SBPSY_l; sfb++)
+ if (gi->scalefac[sfb] > 0)
+ gi->scalefac[sfb] -= pretab[sfb];
+
+ gi->preflag = recalc = 1;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ l3_side->scfsi[ch][i] = 0;
+
+ if (cfg->mode_gr == 2 && gr == 1
+ && l3_side->tt[0][ch].block_type != SHORT_TYPE
+ && l3_side->tt[1][ch].block_type != SHORT_TYPE) {
+ scfsi_calc(ch, l3_side);
+ recalc = 0;
+ }
+ for (sfb = 0; sfb < gi->sfbmax; sfb++) {
+ if (gi->scalefac[sfb] == -2) {
+ gi->scalefac[sfb] = 0; /* if anything goes, then 0 is a good choice */
+ }
+ }
+ if (recalc) {
+ (void) scale_bitcount(gfc, gi);
+ }
+}
+
+
+#ifndef NDEBUG
+static int
+all_scalefactors_not_negative(int const *scalefac, int n)
+{
+ int i;
+ for (i = 0; i < n; ++i) {
+ if (scalefac[i] < 0)
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+
+/* number of bits used to encode scalefacs */
+
+/* 18*slen1_tab[i] + 18*slen2_tab[i] */
+static const int scale_short[16] = {
+ 0, 18, 36, 54, 54, 36, 54, 72, 54, 72, 90, 72, 90, 108, 108, 126
+};
+
+/* 17*slen1_tab[i] + 18*slen2_tab[i] */
+static const int scale_mixed[16] = {
+ 0, 18, 36, 54, 51, 35, 53, 71, 52, 70, 88, 69, 87, 105, 104, 122
+};
+
+/* 11*slen1_tab[i] + 10*slen2_tab[i] */
+static const int scale_long[16] = {
+ 0, 10, 20, 30, 33, 21, 31, 41, 32, 42, 52, 43, 53, 63, 64, 74
+};
+
+
+/*************************************************************************/
+/* scale_bitcount */
+/*************************************************************************/
+
+/* Also calculates the number of bits necessary to code the scalefactors. */
+
+static int
+mpeg1_scale_bitcount(const lame_internal_flags * gfc, gr_info * const cod_info)
+{
+ int k, sfb, max_slen1 = 0, max_slen2 = 0;
+
+ /* maximum values */
+ const int *tab;
+ int *const scalefac = cod_info->scalefac;
+
+ (void) gfc;
+ assert(all_scalefactors_not_negative(scalefac, cod_info->sfbmax));
+
+ if (cod_info->block_type == SHORT_TYPE) {
+ tab = scale_short;
+ if (cod_info->mixed_block_flag)
+ tab = scale_mixed;
+ }
+ else { /* block_type == 1,2,or 3 */
+ tab = scale_long;
+ if (!cod_info->preflag) {
+ for (sfb = 11; sfb < SBPSY_l; sfb++)
+ if (scalefac[sfb] < pretab[sfb])
+ break;
+
+ if (sfb == SBPSY_l) {
+ cod_info->preflag = 1;
+ for (sfb = 11; sfb < SBPSY_l; sfb++)
+ scalefac[sfb] -= pretab[sfb];
+ }
+ }
+ }
+
+ for (sfb = 0; sfb < cod_info->sfbdivide; sfb++)
+ if (max_slen1 < scalefac[sfb])
+ max_slen1 = scalefac[sfb];
+
+ for (; sfb < cod_info->sfbmax; sfb++)
+ if (max_slen2 < scalefac[sfb])
+ max_slen2 = scalefac[sfb];
+
+ /* from Takehiro TOMINAGA 10/99
+ * loop over *all* posible values of scalefac_compress to find the
+ * one which uses the smallest number of bits. ISO would stop
+ * at first valid index */
+ cod_info->part2_length = LARGE_BITS;
+ for (k = 0; k < 16; k++) {
+ if (max_slen1 < slen1_n[k] && max_slen2 < slen2_n[k]
+ && cod_info->part2_length > tab[k]) {
+ cod_info->part2_length = tab[k];
+ cod_info->scalefac_compress = k;
+ }
+ }
+ return cod_info->part2_length == LARGE_BITS;
+}
+
+
+
+/*
+ table of largest scalefactor values for MPEG2
+*/
+static const int max_range_sfac_tab[6][4] = {
+ {15, 15, 7, 7},
+ {15, 15, 7, 0},
+ {7, 3, 0, 0},
+ {15, 31, 31, 0},
+ {7, 7, 7, 0},
+ {3, 3, 0, 0}
+};
+
+
+
+
+/*************************************************************************/
+/* scale_bitcount_lsf */
+/*************************************************************************/
+
+/* Also counts the number of bits to encode the scalefacs but for MPEG 2 */
+/* Lower sampling frequencies (24, 22.05 and 16 kHz.) */
+
+/* This is reverse-engineered from section 2.4.3.2 of the MPEG2 IS, */
+/* "Audio Decoding Layer III" */
+
+static int
+mpeg2_scale_bitcount(const lame_internal_flags * gfc, gr_info * const cod_info)
+{
+ int table_number, row_in_table, partition, nr_sfb, window, over;
+ int i, sfb, max_sfac[4];
+ const int *partition_table;
+ int const *const scalefac = cod_info->scalefac;
+
+ /*
+ Set partition table. Note that should try to use table one,
+ but do not yet...
+ */
+ if (cod_info->preflag)
+ table_number = 2;
+ else
+ table_number = 0;
+
+ for (i = 0; i < 4; i++)
+ max_sfac[i] = 0;
+
+ if (cod_info->block_type == SHORT_TYPE) {
+ row_in_table = 1;
+ partition_table = &nr_of_sfb_block[table_number][row_in_table][0];
+ for (sfb = 0, partition = 0; partition < 4; partition++) {
+ nr_sfb = partition_table[partition] / 3;
+ for (i = 0; i < nr_sfb; i++, sfb++)
+ for (window = 0; window < 3; window++)
+ if (scalefac[sfb * 3 + window] > max_sfac[partition])
+ max_sfac[partition] = scalefac[sfb * 3 + window];
+ }
+ }
+ else {
+ row_in_table = 0;
+ partition_table = &nr_of_sfb_block[table_number][row_in_table][0];
+ for (sfb = 0, partition = 0; partition < 4; partition++) {
+ nr_sfb = partition_table[partition];
+ for (i = 0; i < nr_sfb; i++, sfb++)
+ if (scalefac[sfb] > max_sfac[partition])
+ max_sfac[partition] = scalefac[sfb];
+ }
+ }
+
+ for (over = 0, partition = 0; partition < 4; partition++) {
+ if (max_sfac[partition] > max_range_sfac_tab[table_number][partition])
+ over++;
+ }
+ if (!over) {
+ /*
+ Since no bands have been over-amplified, we can set scalefac_compress
+ and slen[] for the formatter
+ */
+ static const int log2tab[] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 };
+
+ int slen1, slen2, slen3, slen4;
+
+ cod_info->sfb_partition_table = nr_of_sfb_block[table_number][row_in_table];
+ for (partition = 0; partition < 4; partition++)
+ cod_info->slen[partition] = log2tab[max_sfac[partition]];
+
+ /* set scalefac_compress */
+ slen1 = cod_info->slen[0];
+ slen2 = cod_info->slen[1];
+ slen3 = cod_info->slen[2];
+ slen4 = cod_info->slen[3];
+
+ switch (table_number) {
+ case 0:
+ cod_info->scalefac_compress = (((slen1 * 5) + slen2) << 4)
+ + (slen3 << 2)
+ + slen4;
+ break;
+
+ case 1:
+ cod_info->scalefac_compress = 400 + (((slen1 * 5) + slen2) << 2)
+ + slen3;
+ break;
+
+ case 2:
+ cod_info->scalefac_compress = 500 + (slen1 * 3) + slen2;
+ break;
+
+ default:
+ ERRORF(gfc, "intensity stereo not implemented yet\n");
+ break;
+ }
+ }
+#ifdef DEBUG
+ if (over)
+ ERRORF(gfc, "---WARNING !! Amplification of some bands over limits\n");
+#endif
+ if (!over) {
+ assert(cod_info->sfb_partition_table);
+ cod_info->part2_length = 0;
+ for (partition = 0; partition < 4; partition++)
+ cod_info->part2_length +=
+ cod_info->slen[partition] * cod_info->sfb_partition_table[partition];
+ }
+ return over;
+}
+
+
+int
+scale_bitcount(const lame_internal_flags * gfc, gr_info * cod_info)
+{
+ if (gfc->cfg.mode_gr == 2) {
+ return mpeg1_scale_bitcount(gfc, cod_info);
+ }
+ else {
+ return mpeg2_scale_bitcount(gfc, cod_info);
+ }
+}
+
+
+#ifdef MMX_choose_table
+extern int choose_table_MMX(const int *ix, const int *const end, int *const s);
+#endif
+
+void
+huffman_init(lame_internal_flags * const gfc)
+{
+ int i;
+
+ gfc->choose_table = choose_table_nonMMX;
+
+#ifdef MMX_choose_table
+ if (gfc->CPU_features.MMX) {
+ gfc->choose_table = choose_table_MMX;
+ }
+#endif
+
+ for (i = 2; i <= 576; i += 2) {
+ int scfb_anz = 0, bv_index;
+ while (gfc->scalefac_band.l[++scfb_anz] < i);
+
+ bv_index = subdv_table[scfb_anz].region0_count;
+ while (gfc->scalefac_band.l[bv_index + 1] > i)
+ bv_index--;
+
+ if (bv_index < 0) {
+ /* this is an indication that everything is going to
+ be encoded as region0: bigvalues < region0 < region1
+ so lets set region0, region1 to some value larger
+ than bigvalues */
+ bv_index = subdv_table[scfb_anz].region0_count;
+ }
+
+ gfc->sv_qnt.bv_scf[i - 2] = bv_index;
+
+ bv_index = subdv_table[scfb_anz].region1_count;
+ while (gfc->scalefac_band.l[bv_index + gfc->sv_qnt.bv_scf[i - 2] + 2] > i)
+ bv_index--;
+
+ if (bv_index < 0) {
+ bv_index = subdv_table[scfb_anz].region1_count;
+ }
+
+ gfc->sv_qnt.bv_scf[i - 1] = bv_index;
+ }
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/util.c b/libnative/src/main/cpp/module/mp3/lame/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..e0d48d3f6978bb1e736e1a6b71bc56d7d92907d7
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/util.c
@@ -0,0 +1,1009 @@
+/*
+ * lame utility library source file
+ *
+ * Copyright (c) 1999 Albert L Faber
+ * Copyright (c) 2000-2005 Alexander Leidinger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: util.c,v 1.154.2.1 2012/01/08 23:49:58 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "tables.h"
+
+#define PRECOMPUTE
+#if defined(__FreeBSD__) && !defined(__alpha__)
+# include
+#endif
+
+
+/***********************************************************************
+*
+* Global Function Definitions
+*
+***********************************************************************/
+/*empty and close mallocs in gfc */
+
+void
+free_id3tag(lame_internal_flags * const gfc)
+{
+ if (gfc->tag_spec.title != 0) {
+ free(gfc->tag_spec.title);
+ gfc->tag_spec.title = 0;
+ }
+ if (gfc->tag_spec.artist != 0) {
+ free(gfc->tag_spec.artist);
+ gfc->tag_spec.artist = 0;
+ }
+ if (gfc->tag_spec.album != 0) {
+ free(gfc->tag_spec.album);
+ gfc->tag_spec.album = 0;
+ }
+ if (gfc->tag_spec.comment != 0) {
+ free(gfc->tag_spec.comment);
+ gfc->tag_spec.comment = 0;
+ }
+
+ if (gfc->tag_spec.albumart != 0) {
+ free(gfc->tag_spec.albumart);
+ gfc->tag_spec.albumart = 0;
+ gfc->tag_spec.albumart_size = 0;
+ gfc->tag_spec.albumart_mimetype = MIMETYPE_NONE;
+ }
+ if (gfc->tag_spec.v2_head != 0) {
+ FrameDataNode *node = gfc->tag_spec.v2_head;
+ do {
+ void *p = node->dsc.ptr.b;
+ void *q = node->txt.ptr.b;
+ void *r = node;
+ node = node->nxt;
+ free(p);
+ free(q);
+ free(r);
+ } while (node != 0);
+ gfc->tag_spec.v2_head = 0;
+ gfc->tag_spec.v2_tail = 0;
+ }
+}
+
+
+static void
+free_global_data(lame_internal_flags * gfc)
+{
+ if (gfc && gfc->cd_psy) {
+ if (gfc->cd_psy->l.s3) {
+ /* XXX allocated in psymodel_init() */
+ free(gfc->cd_psy->l.s3);
+ }
+ if (gfc->cd_psy->s.s3) {
+ /* XXX allocated in psymodel_init() */
+ free(gfc->cd_psy->s.s3);
+ }
+ free(gfc->cd_psy);
+ gfc->cd_psy = 0;
+ }
+}
+
+
+void
+freegfc(lame_internal_flags * const gfc)
+{ /* bit stream structure */
+ int i;
+
+
+ for (i = 0; i <= 2 * BPC; i++)
+ if (gfc->sv_enc.blackfilt[i] != NULL) {
+ free(gfc->sv_enc.blackfilt[i]);
+ gfc->sv_enc.blackfilt[i] = NULL;
+ }
+ if (gfc->sv_enc.inbuf_old[0]) {
+ free(gfc->sv_enc.inbuf_old[0]);
+ gfc->sv_enc.inbuf_old[0] = NULL;
+ }
+ if (gfc->sv_enc.inbuf_old[1]) {
+ free(gfc->sv_enc.inbuf_old[1]);
+ gfc->sv_enc.inbuf_old[1] = NULL;
+ }
+
+ if (gfc->bs.buf != NULL) {
+ free(gfc->bs.buf);
+ gfc->bs.buf = NULL;
+ }
+
+ if (gfc->VBR_seek_table.bag) {
+ free(gfc->VBR_seek_table.bag);
+ gfc->VBR_seek_table.bag = NULL;
+ gfc->VBR_seek_table.size = 0;
+ }
+ if (gfc->ATH) {
+ free(gfc->ATH);
+ }
+ if (gfc->sv_rpg.rgdata) {
+ free(gfc->sv_rpg.rgdata);
+ }
+ if (gfc->sv_enc.in_buffer_0) {
+ free(gfc->sv_enc.in_buffer_0);
+ }
+ if (gfc->sv_enc.in_buffer_1) {
+ free(gfc->sv_enc.in_buffer_1);
+ }
+ free_id3tag(gfc);
+
+#ifdef DECODE_ON_THE_FLY
+ if (gfc->hip) {
+ hip_decode_exit(gfc->hip);
+ gfc->hip = 0;
+ }
+#endif
+
+ free_global_data(gfc);
+
+ free(gfc);
+}
+
+void
+malloc_aligned(aligned_pointer_t * ptr, unsigned int size, unsigned int bytes)
+{
+ if (ptr) {
+ if (!ptr->pointer) {
+ ptr->pointer = malloc(size + bytes);
+ if (bytes > 0) {
+ ptr->aligned = (void *) ((((size_t) ptr->pointer + bytes - 1) / bytes) * bytes);
+ }
+ else {
+ ptr->aligned = ptr->pointer;
+ }
+ }
+ }
+}
+
+void
+free_aligned(aligned_pointer_t * ptr)
+{
+ if (ptr) {
+ if (ptr->pointer) {
+ free(ptr->pointer);
+ ptr->pointer = 0;
+ ptr->aligned = 0;
+ }
+ }
+}
+
+/*those ATH formulas are returning
+their minimum value for input = -1*/
+
+static FLOAT
+ATHformula_GB(FLOAT f, FLOAT value, FLOAT f_min, FLOAT f_max)
+{
+ /* from Painter & Spanias
+ modified by Gabriel Bouvigne to better fit the reality
+ ath = 3.640 * pow(f,-0.8)
+ - 6.800 * exp(-0.6*pow(f-3.4,2.0))
+ + 6.000 * exp(-0.15*pow(f-8.7,2.0))
+ + 0.6* 0.001 * pow(f,4.0);
+
+
+ In the past LAME was using the Painter &Spanias formula.
+ But we had some recurrent problems with HF content.
+ We measured real ATH values, and found the older formula
+ to be inacurate in the higher part. So we made this new
+ formula and this solved most of HF problematic testcases.
+ The tradeoff is that in VBR mode it increases a lot the
+ bitrate. */
+
+
+/*this curve can be udjusted according to the VBR scale:
+it adjusts from something close to Painter & Spanias
+on V9 up to Bouvigne's formula for V0. This way the VBR
+bitrate is more balanced according to the -V value.*/
+
+ FLOAT ath;
+
+ /* the following Hack allows to ask for the lowest value */
+ if (f < -.3)
+ f = 3410;
+
+ f /= 1000; /* convert to khz */
+ f = Max(f_min, f);
+ f = Min(f_max, f);
+
+ ath = 3.640 * pow(f, -0.8)
+ - 6.800 * exp(-0.6 * pow(f - 3.4, 2.0))
+ + 6.000 * exp(-0.15 * pow(f - 8.7, 2.0))
+ + (0.6 + 0.04 * value) * 0.001 * pow(f, 4.0);
+ return ath;
+}
+
+
+
+FLOAT
+ATHformula(SessionConfig_t const *cfg, FLOAT f)
+{
+ FLOAT ath;
+ switch (cfg->ATHtype) {
+ case 0:
+ ath = ATHformula_GB(f, 9, 0.1f, 24.0f);
+ break;
+ case 1:
+ ath = ATHformula_GB(f, -1, 0.1f, 24.0f); /*over sensitive, should probably be removed */
+ break;
+ case 2:
+ ath = ATHformula_GB(f, 0, 0.1f, 24.0f);
+ break;
+ case 3:
+ ath = ATHformula_GB(f, 1, 0.1f, 24.0f) + 6; /*modification of GB formula by Roel */
+ break;
+ case 4:
+ ath = ATHformula_GB(f, cfg->ATHcurve, 0.1f, 24.0f);
+ break;
+ case 5:
+ ath = ATHformula_GB(f, cfg->ATHcurve, 3.41f, 16.1f);
+ break;
+ default:
+ ath = ATHformula_GB(f, 0, 0.1f, 24.0f);
+ break;
+ }
+ return ath;
+}
+
+/* see for example "Zwicker: Psychoakustik, 1982; ISBN 3-540-11401-7 */
+FLOAT
+freq2bark(FLOAT freq)
+{
+ /* input: freq in hz output: barks */
+ if (freq < 0)
+ freq = 0;
+ freq = freq * 0.001;
+ return 13.0 * atan(.76 * freq) + 3.5 * atan(freq * freq / (7.5 * 7.5));
+}
+
+#if 0
+extern FLOAT freq2cbw(FLOAT freq);
+
+/* see for example "Zwicker: Psychoakustik, 1982; ISBN 3-540-11401-7 */
+FLOAT
+freq2cbw(FLOAT freq)
+{
+ /* input: freq in hz output: critical band width */
+ freq = freq * 0.001;
+ return 25 + 75 * pow(1 + 1.4 * (freq * freq), 0.69);
+}
+
+#endif
+
+
+
+
+#define ABS(A) (((A)>0) ? (A) : -(A))
+
+int
+FindNearestBitrate(int bRate, /* legal rates from 8 to 320 */
+ int version, int samplerate)
+{ /* MPEG-1 or MPEG-2 LSF */
+ int bitrate;
+ int i;
+
+ if (samplerate < 16000)
+ version = 2;
+
+ bitrate = bitrate_table[version][1];
+
+ for (i = 2; i <= 14; i++) {
+ if (bitrate_table[version][i] > 0) {
+ if (ABS(bitrate_table[version][i] - bRate) < ABS(bitrate - bRate))
+ bitrate = bitrate_table[version][i];
+ }
+ }
+ return bitrate;
+}
+
+
+
+
+
+#ifndef Min
+#define Min(A, B) ((A) < (B) ? (A) : (B))
+#endif
+#ifndef Max
+#define Max(A, B) ((A) > (B) ? (A) : (B))
+#endif
+
+
+/* Used to find table index when
+ * we need bitrate-based values
+ * determined using tables
+ *
+ * bitrate in kbps
+ *
+ * Gabriel Bouvigne 2002-11-03
+ */
+int
+nearestBitrateFullIndex(uint16_t bitrate)
+{
+ /* borrowed from DM abr presets */
+
+ const int full_bitrate_table[] =
+ { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
+
+
+ int lower_range = 0, lower_range_kbps = 0, upper_range = 0, upper_range_kbps = 0;
+
+
+ int b;
+
+
+ /* We assume specified bitrate will be 320kbps */
+ upper_range_kbps = full_bitrate_table[16];
+ upper_range = 16;
+ lower_range_kbps = full_bitrate_table[16];
+ lower_range = 16;
+
+ /* Determine which significant bitrates the value specified falls between,
+ * if loop ends without breaking then we were correct above that the value was 320
+ */
+ for (b = 0; b < 16; b++) {
+ if ((Max(bitrate, full_bitrate_table[b + 1])) != bitrate) {
+ upper_range_kbps = full_bitrate_table[b + 1];
+ upper_range = b + 1;
+ lower_range_kbps = full_bitrate_table[b];
+ lower_range = (b);
+ break; /* We found upper range */
+ }
+ }
+
+ /* Determine which range the value specified is closer to */
+ if ((upper_range_kbps - bitrate) > (bitrate - lower_range_kbps)) {
+ return lower_range;
+ }
+ return upper_range;
+}
+
+
+
+
+
+/* map frequency to a valid MP3 sample frequency
+ *
+ * Robert Hegemann 2000-07-01
+ */
+int
+map2MP3Frequency(int freq)
+{
+ if (freq <= 8000)
+ return 8000;
+ if (freq <= 11025)
+ return 11025;
+ if (freq <= 12000)
+ return 12000;
+ if (freq <= 16000)
+ return 16000;
+ if (freq <= 22050)
+ return 22050;
+ if (freq <= 24000)
+ return 24000;
+ if (freq <= 32000)
+ return 32000;
+ if (freq <= 44100)
+ return 44100;
+
+ return 48000;
+}
+
+int
+BitrateIndex(int bRate, /* legal rates from 32 to 448 kbps */
+ int version, /* MPEG-1 or MPEG-2/2.5 LSF */
+ int samplerate)
+{ /* convert bitrate in kbps to index */
+ int i;
+ if (samplerate < 16000)
+ version = 2;
+ for (i = 0; i <= 14; i++) {
+ if (bitrate_table[version][i] > 0) {
+ if (bitrate_table[version][i] == bRate) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+/* convert samp freq in Hz to index */
+
+int
+SmpFrqIndex(int sample_freq, int *const version)
+{
+ switch (sample_freq) {
+ case 44100:
+ *version = 1;
+ return 0;
+ case 48000:
+ *version = 1;
+ return 1;
+ case 32000:
+ *version = 1;
+ return 2;
+ case 22050:
+ *version = 0;
+ return 0;
+ case 24000:
+ *version = 0;
+ return 1;
+ case 16000:
+ *version = 0;
+ return 2;
+ case 11025:
+ *version = 0;
+ return 0;
+ case 12000:
+ *version = 0;
+ return 1;
+ case 8000:
+ *version = 0;
+ return 2;
+ default:
+ *version = 0;
+ return -1;
+ }
+}
+
+
+/*****************************************************************************
+*
+* End of bit_stream.c package
+*
+*****************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/* resampling via FIR filter, blackman window */
+inline static FLOAT
+blackman(FLOAT x, FLOAT fcn, int l)
+{
+ /* This algorithm from:
+ SIGNAL PROCESSING ALGORITHMS IN FORTRAN AND C
+ S.D. Stearns and R.A. David, Prentice-Hall, 1992
+ */
+ FLOAT bkwn, x2;
+ FLOAT const wcn = (PI * fcn);
+
+ x /= l;
+ if (x < 0)
+ x = 0;
+ if (x > 1)
+ x = 1;
+ x2 = x - .5;
+
+ bkwn = 0.42 - 0.5 * cos(2 * x * PI) + 0.08 * cos(4 * x * PI);
+ if (fabs(x2) < 1e-9)
+ return wcn / PI;
+ else
+ return (bkwn * sin(l * wcn * x2) / (PI * l * x2));
+
+
+}
+
+
+
+
+/* gcd - greatest common divisor */
+/* Joint work of Euclid and M. Hendry */
+
+static int
+gcd(int i, int j)
+{
+ /* assert ( i > 0 && j > 0 ); */
+ return j ? gcd(j, i % j) : i;
+}
+
+
+
+static int
+fill_buffer_resample(lame_internal_flags * gfc,
+ sample_t * outbuf,
+ int desired_len, sample_t const *inbuf, int len, int *num_used, int ch)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ EncStateVar_t *esv = &gfc->sv_enc;
+ double resample_ratio = (double)cfg->samplerate_in / (double)cfg->samplerate_out;
+ int BLACKSIZE;
+ FLOAT offset, xvalue;
+ int i, j = 0, k;
+ int filter_l;
+ FLOAT fcn, intratio;
+ FLOAT *inbuf_old;
+ int bpc; /* number of convolution functions to pre-compute */
+ bpc = cfg->samplerate_out / gcd(cfg->samplerate_out, cfg->samplerate_in);
+ if (bpc > BPC)
+ bpc = BPC;
+
+ intratio = (fabs(resample_ratio - floor(.5 + resample_ratio)) < .0001);
+ fcn = 1.00 / resample_ratio;
+ if (fcn > 1.00)
+ fcn = 1.00;
+ filter_l = 31; /* must be odd */
+ filter_l += intratio; /* unless resample_ratio=int, it must be even */
+
+
+ BLACKSIZE = filter_l + 1; /* size of data needed for FIR */
+
+ if (gfc->fill_buffer_resample_init == 0) {
+ esv->inbuf_old[0] = calloc(BLACKSIZE, sizeof(esv->inbuf_old[0][0]));
+ esv->inbuf_old[1] = calloc(BLACKSIZE, sizeof(esv->inbuf_old[0][0]));
+ for (i = 0; i <= 2 * bpc; ++i)
+ esv->blackfilt[i] = calloc(BLACKSIZE, sizeof(esv->blackfilt[0][0]));
+
+ esv->itime[0] = 0;
+ esv->itime[1] = 0;
+
+ /* precompute blackman filter coefficients */
+ for (j = 0; j <= 2 * bpc; j++) {
+ FLOAT sum = 0.;
+ offset = (j - bpc) / (2. * bpc);
+ for (i = 0; i <= filter_l; i++)
+ sum += esv->blackfilt[j][i] = blackman(i - offset, fcn, filter_l);
+ for (i = 0; i <= filter_l; i++)
+ esv->blackfilt[j][i] /= sum;
+ }
+ gfc->fill_buffer_resample_init = 1;
+ }
+
+ inbuf_old = esv->inbuf_old[ch];
+
+ /* time of j'th element in inbuf = itime + j/ifreq; */
+ /* time of k'th element in outbuf = j/ofreq */
+ for (k = 0; k < desired_len; k++) {
+ double time0 = k * resample_ratio; /* time of k'th output sample */
+ int joff;
+
+ j = floor(time0 - esv->itime[ch]);
+
+ /* check if we need more input data */
+ if ((filter_l + j - filter_l / 2) >= len)
+ break;
+
+ /* blackman filter. by default, window centered at j+.5(filter_l%2) */
+ /* but we want a window centered at time0. */
+ offset = (time0 - esv->itime[ch] - (j + .5 * (filter_l % 2)));
+ assert(fabs(offset) <= .501);
+
+ /* find the closest precomputed window for this offset: */
+ joff = floor((offset * 2 * bpc) + bpc + .5);
+
+ xvalue = 0.;
+ for (i = 0; i <= filter_l; ++i) {
+ int const j2 = i + j - filter_l / 2;
+ sample_t y;
+ assert(j2 < len);
+ assert(j2 + BLACKSIZE >= 0);
+ y = (j2 < 0) ? inbuf_old[BLACKSIZE + j2] : inbuf[j2];
+#ifdef PRECOMPUTE
+ xvalue += y * esv->blackfilt[joff][i];
+#else
+ xvalue += y * blackman(i - offset, fcn, filter_l); /* very slow! */
+#endif
+ }
+ outbuf[k] = xvalue;
+ }
+
+
+ /* k = number of samples added to outbuf */
+ /* last k sample used data from [j-filter_l/2,j+filter_l-filter_l/2] */
+
+ /* how many samples of input data were used: */
+ *num_used = Min(len, filter_l + j - filter_l / 2);
+
+ /* adjust our input time counter. Incriment by the number of samples used,
+ * then normalize so that next output sample is at time 0, next
+ * input buffer is at time itime[ch] */
+ esv->itime[ch] += *num_used - k * resample_ratio;
+
+ /* save the last BLACKSIZE samples into the inbuf_old buffer */
+ if (*num_used >= BLACKSIZE) {
+ for (i = 0; i < BLACKSIZE; i++)
+ inbuf_old[i] = inbuf[*num_used + i - BLACKSIZE];
+ }
+ else {
+ /* shift in *num_used samples into inbuf_old */
+ int const n_shift = BLACKSIZE - *num_used; /* number of samples to shift */
+
+ /* shift n_shift samples by *num_used, to make room for the
+ * num_used new samples */
+ for (i = 0; i < n_shift; ++i)
+ inbuf_old[i] = inbuf_old[i + *num_used];
+
+ /* shift in the *num_used samples */
+ for (j = 0; i < BLACKSIZE; ++i, ++j)
+ inbuf_old[i] = inbuf[j];
+
+ assert(j == *num_used);
+ }
+ return k; /* return the number samples created at the new samplerate */
+}
+
+int
+isResamplingNecessary(SessionConfig_t const* cfg)
+{
+ int const l = cfg->samplerate_out * 0.9995f;
+ int const h = cfg->samplerate_out * 1.0005f;
+ return (cfg->samplerate_in < l) || (h < cfg->samplerate_in) ? 1 : 0;
+}
+
+/* copy in new samples from in_buffer into mfbuf, with resampling
+ if necessary. n_in = number of samples from the input buffer that
+ were used. n_out = number of samples copied into mfbuf */
+
+void
+fill_buffer(lame_internal_flags * gfc,
+ sample_t * const mfbuf[2], sample_t const * const in_buffer[2], int nsamples, int *n_in, int *n_out)
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int mf_size = gfc->sv_enc.mf_size;
+ int framesize = 576 * cfg->mode_gr;
+ int nout, ch = 0;
+ int nch = cfg->channels_out;
+
+ /* copy in new samples into mfbuf, with resampling if necessary */
+ if (isResamplingNecessary(cfg)) {
+ do {
+ nout =
+ fill_buffer_resample(gfc, &mfbuf[ch][mf_size],
+ framesize, in_buffer[ch], nsamples, n_in, ch);
+ } while (++ch < nch);
+ *n_out = nout;
+ }
+ else {
+ nout = Min(framesize, nsamples);
+ do {
+ memcpy(&mfbuf[ch][mf_size], &in_buffer[ch][0], nout * sizeof(mfbuf[0][0]));
+ } while (++ch < nch);
+ *n_out = nout;
+ *n_in = nout;
+ }
+}
+
+
+
+
+
+
+
+/***********************************************************************
+*
+* Message Output
+*
+***********************************************************************/
+
+void
+lame_report_def(const char *format, va_list args)
+{
+ (void) vfprintf(stderr, format, args);
+ fflush(stderr); /* an debug function should flush immediately */
+}
+
+void
+lame_report_fnc(lame_report_function print_f, const char *format, ...)
+{
+ if (print_f) {
+ va_list args;
+ va_start(args, format);
+ print_f(format, args);
+ va_end(args);
+ }
+}
+
+
+void
+lame_debugf(const lame_internal_flags* gfc, const char *format, ...)
+{
+ if (gfc && gfc->report_dbg) {
+ va_list args;
+ va_start(args, format);
+ gfc->report_dbg(format, args);
+ va_end(args);
+ }
+}
+
+
+void
+lame_msgf(const lame_internal_flags* gfc, const char *format, ...)
+{
+ if (gfc && gfc->report_msg) {
+ va_list args;
+ va_start(args, format);
+ gfc->report_msg(format, args);
+ va_end(args);
+ }
+}
+
+
+void
+lame_errorf(const lame_internal_flags* gfc, const char *format, ...)
+{
+ if (gfc && gfc->report_err) {
+ va_list args;
+ va_start(args, format);
+ gfc->report_err(format, args);
+ va_end(args);
+ }
+}
+
+
+
+/***********************************************************************
+ *
+ * routines to detect CPU specific features like 3DNow, MMX, SSE
+ *
+ * donated by Frank Klemm
+ * added Robert Hegemann 2000-10-10
+ *
+ ***********************************************************************/
+
+#ifdef HAVE_NASM
+extern int has_MMX_nasm(void);
+extern int has_3DNow_nasm(void);
+extern int has_SSE_nasm(void);
+extern int has_SSE2_nasm(void);
+#endif
+
+int
+has_MMX(void)
+{
+#ifdef HAVE_NASM
+ return has_MMX_nasm();
+#else
+ return 0; /* don't know, assume not */
+#endif
+}
+
+int
+has_3DNow(void)
+{
+#ifdef HAVE_NASM
+ return has_3DNow_nasm();
+#else
+ return 0; /* don't know, assume not */
+#endif
+}
+
+int
+has_SSE(void)
+{
+#ifdef HAVE_NASM
+ return has_SSE_nasm();
+#else
+#if defined( _M_X64 ) || defined( MIN_ARCH_SSE )
+ return 1;
+#else
+ return 0; /* don't know, assume not */
+#endif
+#endif
+}
+
+int
+has_SSE2(void)
+{
+#ifdef HAVE_NASM
+ return has_SSE2_nasm();
+#else
+#if defined( _M_X64 ) || defined( MIN_ARCH_SSE )
+ return 1;
+#else
+ return 0; /* don't know, assume not */
+#endif
+#endif
+}
+
+void
+disable_FPE(void)
+{
+/* extremly system dependent stuff, move to a lib to make the code readable */
+/*==========================================================================*/
+
+
+
+ /*
+ * Disable floating point exceptions
+ */
+
+
+
+
+#if defined(__FreeBSD__) && !defined(__alpha__)
+ {
+ /* seet floating point mask to the Linux default */
+ fp_except_t mask;
+ mask = fpgetmask();
+ /* if bit is set, we get SIGFPE on that error! */
+ fpsetmask(mask & ~(FP_X_INV | FP_X_DZ));
+ /* DEBUGF("FreeBSD mask is 0x%x\n",mask); */
+ }
+#endif
+
+#if defined(__riscos__) && !defined(ABORTFP)
+ /* Disable FPE's under RISC OS */
+ /* if bit is set, we disable trapping that error! */
+ /* _FPE_IVO : invalid operation */
+ /* _FPE_DVZ : divide by zero */
+ /* _FPE_OFL : overflow */
+ /* _FPE_UFL : underflow */
+ /* _FPE_INX : inexact */
+ DisableFPETraps(_FPE_IVO | _FPE_DVZ | _FPE_OFL);
+#endif
+
+ /*
+ * Debugging stuff
+ * The default is to ignore FPE's, unless compiled with -DABORTFP
+ * so add code below to ENABLE FPE's.
+ */
+
+#if defined(ABORTFP)
+#if defined(_MSC_VER)
+ {
+#if 0
+ /* rh 061207
+ the following fix seems to be a workaround for a problem in the
+ parent process calling LAME. It would be better to fix the broken
+ application => code disabled.
+ */
+
+ /* set affinity to a single CPU. Fix for EAC/lame on SMP systems from
+ "Todd Richmond" */
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ SetProcessAffinityMask(GetCurrentProcess(), si.dwActiveProcessorMask);
+#endif
+#include
+ unsigned int mask;
+ mask = _controlfp(0, 0);
+ mask &= ~(_EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE | _EM_INVALID);
+ mask = _controlfp(mask, _MCW_EM);
+ }
+#elif defined(__CYGWIN__)
+# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw))
+# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw))
+
+# define _EM_INEXACT 0x00000020 /* inexact (precision) */
+# define _EM_UNDERFLOW 0x00000010 /* underflow */
+# define _EM_OVERFLOW 0x00000008 /* overflow */
+# define _EM_ZERODIVIDE 0x00000004 /* zero divide */
+# define _EM_INVALID 0x00000001 /* invalid */
+ {
+ unsigned int mask;
+ _FPU_GETCW(mask);
+ /* Set the FPU control word to abort on most FPEs */
+ mask &= ~(_EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID);
+ _FPU_SETCW(mask);
+ }
+# elif defined(__linux__)
+ {
+
+# include
+# ifndef _FPU_GETCW
+# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw))
+# endif
+# ifndef _FPU_SETCW
+# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw))
+# endif
+
+ /*
+ * Set the Linux mask to abort on most FPE's
+ * if bit is set, we _mask_ SIGFPE on that error!
+ * mask &= ~( _FPU_MASK_IM | _FPU_MASK_ZM | _FPU_MASK_OM | _FPU_MASK_UM );
+ */
+
+ unsigned int mask;
+ _FPU_GETCW(mask);
+ mask &= ~(_FPU_MASK_IM | _FPU_MASK_ZM | _FPU_MASK_OM);
+ _FPU_SETCW(mask);
+ }
+#endif
+#endif /* ABORTFP */
+}
+
+
+
+
+
+#ifdef USE_FAST_LOG
+/***********************************************************************
+ *
+ * Fast Log Approximation for log2, used to approximate every other log
+ * (log10 and log)
+ * maximum absolute error for log10 is around 10-6
+ * maximum *relative* error can be high when x is almost 1 because error/log10(x) tends toward x/e
+ *
+ * use it if typical RESULT values are > 1e-5 (for example if x>1.00001 or x<0.99999)
+ * or if the relative precision in the domain around 1 is not important (result in 1 is exact and 0)
+ *
+ ***********************************************************************/
+
+
+#define LOG2_SIZE (512)
+#define LOG2_SIZE_L2 (9)
+
+static ieee754_float32_t log_table[LOG2_SIZE + 1];
+
+
+
+void
+init_log_table(void)
+{
+ int j;
+ static int init = 0;
+
+ /* Range for log2(x) over [1,2[ is [0,1[ */
+ assert((1 << LOG2_SIZE_L2) == LOG2_SIZE);
+
+ if (!init) {
+ for (j = 0; j < LOG2_SIZE + 1; j++)
+ log_table[j] = log(1.0f + j / (ieee754_float32_t) LOG2_SIZE) / log(2.0f);
+ }
+ init = 1;
+}
+
+
+
+ieee754_float32_t
+fast_log2(ieee754_float32_t x)
+{
+ ieee754_float32_t log2val, partial;
+ union {
+ ieee754_float32_t f;
+ int i;
+ } fi;
+ int mantisse;
+ fi.f = x;
+ mantisse = fi.i & 0x7fffff;
+ log2val = ((fi.i >> 23) & 0xFF) - 0x7f;
+ partial = (mantisse & ((1 << (23 - LOG2_SIZE_L2)) - 1));
+ partial *= 1.0f / ((1 << (23 - LOG2_SIZE_L2)));
+
+
+ mantisse >>= (23 - LOG2_SIZE_L2);
+
+ /* log2val += log_table[mantisse]; without interpolation the results are not good */
+ log2val += log_table[mantisse] * (1.0f - partial) + log_table[mantisse + 1] * partial;
+
+ return log2val;
+}
+
+#else /* Don't use FAST_LOG */
+
+
+void
+init_log_table(void)
+{
+}
+
+
+#endif
+
+/* end of util.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/util.h b/libnative/src/main/cpp/module/mp3/lame/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..8024dfa813e97c96a2978ebb91edbb93b29a0537
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/util.h
@@ -0,0 +1,620 @@
+/*
+ * lame utility library include file
+ *
+ * Copyright (c) 1999 Albert L Faber
+ * Copyright (c) 2008 Robert Hegemann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_UTIL_H
+#define LAME_UTIL_H
+
+#include "l3side.h"
+#include "id3tag.h"
+#include "lame_global_flags.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************************************
+*
+* Global Definitions
+*
+***********************************************************************/
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifdef UINT_MAX
+# define MAX_U_32_NUM UINT_MAX
+#else
+# define MAX_U_32_NUM 0xFFFFFFFF
+#endif
+
+#ifndef PI
+# ifdef M_PI
+# define PI M_PI
+# else
+# define PI 3.14159265358979323846
+# endif
+#endif
+
+
+#ifdef M_LN2
+# define LOG2 M_LN2
+#else
+# define LOG2 0.69314718055994530942
+#endif
+
+#ifdef M_LN10
+# define LOG10 M_LN10
+#else
+# define LOG10 2.30258509299404568402
+#endif
+
+
+#ifdef M_SQRT2
+# define SQRT2 M_SQRT2
+#else
+# define SQRT2 1.41421356237309504880
+#endif
+
+
+#define CRC16_POLYNOMIAL 0x8005
+
+#define MAX_BITS_PER_CHANNEL 4095
+#define MAX_BITS_PER_GRANULE 7680
+
+/* "bit_stream.h" Definitions */
+#define BUFFER_SIZE LAME_MAXMP3BUFFER
+
+#define Min(A, B) ((A) < (B) ? (A) : (B))
+#define Max(A, B) ((A) > (B) ? (A) : (B))
+
+/* log/log10 approximations */
+#ifdef USE_FAST_LOG
+#define FAST_LOG10(x) (fast_log2(x)*(LOG2/LOG10))
+#define FAST_LOG(x) (fast_log2(x)*LOG2)
+#define FAST_LOG10_X(x,y) (fast_log2(x)*(LOG2/LOG10*(y)))
+#define FAST_LOG_X(x,y) (fast_log2(x)*(LOG2*(y)))
+#else
+#define FAST_LOG10(x) log10(x)
+#define FAST_LOG(x) log(x)
+#define FAST_LOG10_X(x,y) (log10(x)*(y))
+#define FAST_LOG_X(x,y) (log(x)*(y))
+#endif
+
+
+ struct replaygain_data;
+#ifndef replaygain_data_defined
+#define replaygain_data_defined
+ typedef struct replaygain_data replaygain_t;
+#endif
+ struct plotting_data;
+#ifndef plotting_data_defined
+#define plotting_data_defined
+ typedef struct plotting_data plotting_data;
+#endif
+
+/***********************************************************************
+*
+* Global Type Definitions
+*
+***********************************************************************/
+
+ typedef struct {
+ void *aligned; /* pointer to ie. 128 bit aligned memory */
+ void *pointer; /* to use with malloc/free */
+ } aligned_pointer_t;
+
+ void malloc_aligned(aligned_pointer_t * ptr, unsigned int size, unsigned int bytes);
+ void free_aligned(aligned_pointer_t * ptr);
+
+
+ typedef void (*iteration_loop_t) (lame_internal_flags * gfc, const FLOAT pe[2][2],
+ const FLOAT ms_ratio[2], const III_psy_ratio ratio[2][2]);
+
+
+ /* "bit_stream.h" Type Definitions */
+
+ typedef struct bit_stream_struc {
+ unsigned char *buf; /* bit stream buffer */
+ int buf_size; /* size of buffer (in number of bytes) */
+ int totbit; /* bit counter of bit stream */
+ int buf_byte_idx; /* pointer to top byte in buffer */
+ int buf_bit_idx; /* pointer to top bit of top byte in buffer */
+
+ /* format of file in rd mode (BINARY/ASCII) */
+ } Bit_stream_struc;
+
+
+
+ typedef struct {
+ int sum; /* what we have seen so far */
+ int seen; /* how many frames we have seen in this chunk */
+ int want; /* how many frames we want to collect into one chunk */
+ int pos; /* actual position in our bag */
+ int size; /* size of our bag */
+ int *bag; /* pointer to our bag */
+ unsigned int nVbrNumFrames;
+ unsigned long nBytesWritten;
+ /* VBR tag data */
+ unsigned int TotalFrameSize;
+ } VBR_seek_info_t;
+
+
+ /**
+ * ATH related stuff, if something new ATH related has to be added,
+ * please plugg it here into the ATH_t struct
+ */
+ typedef struct {
+ int use_adjust; /* method for the auto adjustment */
+ FLOAT aa_sensitivity_p; /* factor for tuning the (sample power)
+ point below which adaptive threshold
+ of hearing adjustment occurs */
+ FLOAT adjust_factor; /* lowering based on peak volume, 1 = no lowering */
+ FLOAT adjust_limit; /* limit for dynamic ATH adjust */
+ FLOAT decay; /* determined to lower x dB each second */
+ FLOAT floor; /* lowest ATH value */
+ FLOAT l[SBMAX_l]; /* ATH for sfbs in long blocks */
+ FLOAT s[SBMAX_s]; /* ATH for sfbs in short blocks */
+ FLOAT psfb21[PSFB21]; /* ATH for partitionned sfb21 in long blocks */
+ FLOAT psfb12[PSFB12]; /* ATH for partitionned sfb12 in short blocks */
+ FLOAT cb_l[CBANDS]; /* ATH for long block convolution bands */
+ FLOAT cb_s[CBANDS]; /* ATH for short block convolution bands */
+ FLOAT eql_w[BLKSIZE / 2]; /* equal loudness weights (based on ATH) */
+ } ATH_t;
+
+ /**
+ * PSY Model related stuff
+ */
+
+ typedef struct {
+ FLOAT masking_lower[CBANDS];
+ FLOAT minval[CBANDS];
+ FLOAT rnumlines[CBANDS];
+ FLOAT mld_cb[CBANDS];
+ FLOAT mld[Max(SBMAX_l,SBMAX_s)];
+ FLOAT bo_weight[Max(SBMAX_l,SBMAX_s)]; /* band weight long scalefactor bands, at transition */
+ FLOAT attack_threshold; /* short block tuning */
+ int s3ind[CBANDS][2];
+ int numlines[CBANDS];
+ int bm[Max(SBMAX_l,SBMAX_s)];
+ int bo[Max(SBMAX_l,SBMAX_s)];
+ int npart;
+ int n_sb; /* SBMAX_l or SBMAX_s */
+ FLOAT *s3;
+ } PsyConst_CB2SB_t;
+
+
+ /**
+ * global data constants
+ */
+ typedef struct {
+ PsyConst_CB2SB_t l;
+ PsyConst_CB2SB_t s;
+ PsyConst_CB2SB_t l_to_s;
+ FLOAT attack_threshold[4];
+ FLOAT decay;
+ int force_short_block_calc;
+ } PsyConst_t;
+
+
+ typedef struct {
+
+ FLOAT nb_l1[4][CBANDS], nb_l2[4][CBANDS];
+ FLOAT nb_s1[4][CBANDS], nb_s2[4][CBANDS];
+
+ III_psy_xmin thm[4];
+ III_psy_xmin en[4];
+
+ /* loudness calculation (for adaptive threshold of hearing) */
+ FLOAT loudness_sq_save[2]; /* account for granule delay of L3psycho_anal */
+
+ FLOAT tot_ener[4];
+
+ FLOAT last_en_subshort[4][9];
+ int last_attacks[4];
+
+ int blocktype_old[2];
+ } PsyStateVar_t;
+
+
+ typedef struct {
+ /* loudness calculation (for adaptive threshold of hearing) */
+ FLOAT loudness_sq[2][2]; /* loudness^2 approx. per granule and channel */
+ } PsyResult_t;
+
+
+ /* variables used by encoder.c */
+ typedef struct {
+ /* variables for newmdct.c */
+ FLOAT sb_sample[2][2][18][SBLIMIT];
+ FLOAT amp_filter[32];
+
+ /* variables used by util.c */
+ /* BPC = maximum number of filter convolution windows to precompute */
+#define BPC 320
+ double itime[2]; /* float precision seems to be not enough */
+ sample_t *inbuf_old[2];
+ sample_t *blackfilt[2 * BPC + 1];
+
+ FLOAT pefirbuf[19];
+
+ /* used for padding */
+ int frac_SpF;
+ int slot_lag;
+
+ /* variables for bitstream.c */
+ /* mpeg1: buffer=511 bytes smallest frame: 96-38(sideinfo)=58
+ * max number of frames in reservoir: 8
+ * mpeg2: buffer=255 bytes. smallest frame: 24-23bytes=1
+ * with VBR, if you are encoding all silence, it is possible to
+ * have 8kbs/24khz frames with 1byte of data each, which means we need
+ * to buffer up to 255 headers! */
+ /* also, max_header_buf has to be a power of two */
+#define MAX_HEADER_BUF 256
+#define MAX_HEADER_LEN 40 /* max size of header is 38 */
+ struct {
+ int write_timing;
+ int ptr;
+ char buf[MAX_HEADER_LEN];
+ } header[MAX_HEADER_BUF];
+
+ int h_ptr;
+ int w_ptr;
+ int ancillary_flag;
+
+ /* variables for reservoir.c */
+ int ResvSize; /* in bits */
+ int ResvMax; /* in bits */
+
+ int in_buffer_nsamples;
+ sample_t *in_buffer_0;
+ sample_t *in_buffer_1;
+
+#ifndef MFSIZE
+# define MFSIZE ( 3*1152 + ENCDELAY - MDCTDELAY )
+#endif
+ sample_t mfbuf[2][MFSIZE];
+
+ int mf_samples_to_encode;
+ int mf_size;
+
+ } EncStateVar_t;
+
+
+ typedef struct {
+ /* simple statistics */
+ int bitrate_channelmode_hist[16][4 + 1];
+ int bitrate_blocktype_hist[16][4 + 1 + 1]; /*norm/start/short/stop/mixed(short)/sum */
+
+ int bitrate_index;
+ int frame_number; /* number of frames encoded */
+ int padding; /* padding for the current frame? */
+ int mode_ext;
+ int encoder_delay;
+ int encoder_padding; /* number of samples of padding appended to input */
+ } EncResult_t;
+
+
+ /* variables used by quantize.c */
+ typedef struct {
+ /* variables for nspsytune */
+ FLOAT longfact[SBMAX_l];
+ FLOAT shortfact[SBMAX_s];
+ FLOAT masking_lower;
+ FLOAT mask_adjust; /* the dbQ stuff */
+ FLOAT mask_adjust_short; /* the dbQ stuff */
+ int OldValue[2];
+ int CurrentStep[2];
+ int pseudohalf[SFBMAX];
+ int sfb21_extra; /* will be set in lame_init_params */
+ int substep_shaping; /* 0 = no substep
+ 1 = use substep shaping at last step(VBR only)
+ (not implemented yet)
+ 2 = use substep inside loop
+ 3 = use substep inside loop and last step
+ */
+
+
+ char bv_scf[576];
+ } QntStateVar_t;
+
+
+ typedef struct {
+ replaygain_t *rgdata;
+ /* ReplayGain */
+ } RpgStateVar_t;
+
+
+ typedef struct {
+ FLOAT noclipScale; /* user-specified scale factor required for preventing clipping */
+ sample_t PeakSample;
+ int RadioGain;
+ int noclipGainChange; /* gain change required for preventing clipping */
+ } RpgResult_t;
+
+
+ typedef struct {
+ int version; /* 0=MPEG-2/2.5 1=MPEG-1 */
+ int samplerate_index;
+ int sideinfo_len;
+
+ int noise_shaping; /* 0 = none
+ 1 = ISO AAC model
+ 2 = allow scalefac_select=1
+ */
+
+ int subblock_gain; /* 0 = no, 1 = yes */
+ int use_best_huffman; /* 0 = no. 1=outside loop 2=inside loop(slow) */
+ int noise_shaping_amp; /* 0 = ISO model: amplify all distorted bands
+ 1 = amplify within 50% of max (on db scale)
+ 2 = amplify only most distorted band
+ 3 = method 1 and refine with method 2
+ */
+
+ int noise_shaping_stop; /* 0 = stop at over=0, all scalefacs amplified or
+ a scalefac has reached max value
+ 1 = stop when all scalefacs amplified or
+ a scalefac has reached max value
+ 2 = stop when all scalefacs amplified
+ */
+
+
+ int full_outer_loop; /* 0 = stop early after 0 distortion found. 1 = full search */
+
+ int lowpassfreq;
+ int highpassfreq;
+ int samplerate_in; /* input_samp_rate in Hz. default=44.1 kHz */
+ int samplerate_out; /* output_samp_rate. */
+ int channels_in; /* number of channels in the input data stream (PCM or decoded PCM) */
+ int channels_out; /* number of channels in the output data stream (not used for decoding) */
+ int mode_gr; /* granules per frame */
+ int force_ms; /* force M/S mode. requires mode=1 */
+
+ int quant_comp;
+ int quant_comp_short;
+
+ int use_temporal_masking_effect;
+ int use_safe_joint_stereo;
+
+ int preset;
+
+ vbr_mode vbr;
+ int vbr_avg_bitrate_kbps;
+ int vbr_min_bitrate_index; /* min bitrate index */
+ int vbr_max_bitrate_index; /* max bitrate index */
+ int avg_bitrate;
+ int enforce_min_bitrate; /* strictly enforce VBR_min_bitrate normaly, it will be violated for analog silence */
+
+ int findReplayGain; /* find the RG value? default=0 */
+ int findPeakSample;
+ int decode_on_the_fly; /* decode on the fly? default=0 */
+ int analysis;
+ int disable_reservoir;
+ int buffer_constraint; /* enforce ISO spec as much as possible */
+ int free_format;
+ int write_lame_tag; /* add Xing VBR tag? */
+
+ int error_protection; /* use 2 bytes per frame for a CRC checksum. default=0 */
+ int copyright; /* mark as copyright. default=0 */
+ int original; /* mark as original. default=1 */
+ int extension; /* the MP3 'private extension' bit. Meaningless */
+ int emphasis; /* Input PCM is emphased PCM (for
+ instance from one of the rarely
+ emphased CDs), it is STRONGLY not
+ recommended to use this, because
+ psycho does not take it into account,
+ and last but not least many decoders
+ don't care about these bits */
+
+
+ MPEG_mode mode;
+ short_block_t short_blocks;
+
+ float interChRatio;
+ float msfix; /* Naoki's adjustment of Mid/Side maskings */
+ float ATH_offset_db;/* add to ATH this many db */
+ float ATH_offset_factor;/* change ATH by this factor, derived from ATH_offset_db */
+ float ATHcurve; /* change ATH formula 4 shape */
+ int ATHtype;
+ int ATHonly; /* only use ATH */
+ int ATHshort; /* only use ATH for short blocks */
+ int noATH; /* disable ATH */
+
+ float ATHfixpoint;
+
+ float adjust_alto_db;
+ float adjust_bass_db;
+ float adjust_treble_db;
+ float adjust_sfb21_db;
+
+ float compression_ratio; /* sizeof(wav file)/sizeof(mp3 file) */
+
+ /* lowpass and highpass filter control */
+ FLOAT lowpass1, lowpass2; /* normalized frequency bounds of passband */
+ FLOAT highpass1, highpass2; /* normalized frequency bounds of passband */
+
+ /* scale input by this amount before encoding at least not used for MP3 decoding */
+ FLOAT pcm_transform[2][2];
+
+ FLOAT minval;
+ } SessionConfig_t;
+
+
+ struct lame_internal_flags {
+
+ /********************************************************************
+ * internal variables NOT set by calling program, and should not be *
+ * modified by the calling program *
+ ********************************************************************/
+
+ /*
+ * Some remarks to the Class_ID field:
+ * The Class ID is an Identifier for a pointer to this struct.
+ * It is very unlikely that a pointer to lame_global_flags has the same 32 bits
+ * in it's structure (large and other special properties, for instance prime).
+ *
+ * To test that the structure is right and initialized, use:
+ * if ( gfc -> Class_ID == LAME_ID ) ...
+ * Other remark:
+ * If you set a flag to 0 for uninit data and 1 for init data, the right test
+ * should be "if (flag == 1)" and NOT "if (flag)". Unintended modification
+ * of this element will be otherwise misinterpreted as an init.
+ */
+# define LAME_ID 0xFFF88E3B
+ unsigned long class_id;
+
+ int lame_encode_frame_init;
+ int iteration_init_init;
+ int fill_buffer_resample_init;
+
+ SessionConfig_t cfg;
+
+ /* variables used by lame.c */
+ Bit_stream_struc bs;
+ III_side_info_t l3_side;
+
+ scalefac_struct scalefac_band;
+
+ PsyStateVar_t sv_psy; /* DATA FROM PSYMODEL.C */
+ PsyResult_t ov_psy;
+ EncStateVar_t sv_enc; /* DATA FROM ENCODER.C */
+ EncResult_t ov_enc;
+ QntStateVar_t sv_qnt; /* DATA FROM QUANTIZE.C */
+
+ RpgStateVar_t sv_rpg;
+ RpgResult_t ov_rpg;
+
+ /* optional ID3 tags, used in id3tag.c */
+ struct id3tag_spec tag_spec;
+ uint16_t nMusicCRC;
+
+ uint16_t _unused;
+
+ /* CPU features */
+ struct {
+ unsigned int MMX:1; /* Pentium MMX, Pentium II...IV, K6, K6-2,
+ K6-III, Athlon */
+ unsigned int AMD_3DNow:1; /* K6-2, K6-III, Athlon */
+ unsigned int SSE:1; /* Pentium III, Pentium 4 */
+ unsigned int SSE2:1; /* Pentium 4, K8 */
+ unsigned int _unused:28;
+ } CPU_features;
+
+
+ VBR_seek_info_t VBR_seek_table; /* used for Xing VBR header */
+
+ ATH_t *ATH; /* all ATH related stuff */
+
+ PsyConst_t *cd_psy;
+
+ /* used by the frame analyzer */
+ plotting_data *pinfo;
+ hip_t hip;
+
+ iteration_loop_t iteration_loop;
+
+ /* functions to replace with CPU feature optimized versions in takehiro.c */
+ int (*choose_table) (const int *ix, const int *const end, int *const s);
+ void (*fft_fht) (FLOAT *, int);
+ void (*init_xrpow_core) (gr_info * const cod_info, FLOAT xrpow[576], int upper,
+ FLOAT * sum);
+
+ lame_report_function report_msg;
+ lame_report_function report_dbg;
+ lame_report_function report_err;
+ };
+
+#ifndef lame_internal_flags_defined
+#define lame_internal_flags_defined
+ typedef struct lame_internal_flags lame_internal_flags;
+#endif
+
+
+/***********************************************************************
+*
+* Global Function Prototype Declarations
+*
+***********************************************************************/
+ void freegfc(lame_internal_flags * const gfc);
+ void free_id3tag(lame_internal_flags * const gfc);
+ extern int BitrateIndex(int, int, int);
+ extern int FindNearestBitrate(int, int, int);
+ extern int map2MP3Frequency(int freq);
+ extern int SmpFrqIndex(int, int *const);
+ extern int nearestBitrateFullIndex(uint16_t brate);
+ extern FLOAT ATHformula(SessionConfig_t const *cfg, FLOAT freq);
+ extern FLOAT freq2bark(FLOAT freq);
+ void disable_FPE(void);
+
+/* log/log10 approximations */
+ extern void init_log_table(void);
+// extern ieee754_float32_t fast_log2(ieee754_float32_t x);
+ extern float fast_log2(float x);
+ int isResamplingNecessary(SessionConfig_t const* cfg);
+
+ void fill_buffer(lame_internal_flags * gfc,
+ sample_t *const mfbuf[2],
+ sample_t const *const in_buffer[2], int nsamples, int *n_in, int *n_out);
+
+/* same as lame_decode1 (look in lame.h), but returns
+ unclipped raw floating-point samples. It is declared
+ here, not in lame.h, because it returns LAME's
+ internal type sample_t. No more than 1152 samples
+ per channel are allowed. */
+ int hip_decode1_unclipped(hip_t hip, unsigned char *mp3buf,
+ size_t len, sample_t pcm_l[], sample_t pcm_r[]);
+
+
+ extern int has_MMX(void);
+ extern int has_3DNow(void);
+ extern int has_SSE(void);
+ extern int has_SSE2(void);
+
+
+
+/***********************************************************************
+*
+* Macros about Message Printing and Exit
+*
+***********************************************************************/
+
+ extern void lame_report_def(const char* format, va_list args);
+ extern void lame_report_fnc(lame_report_function print_f, const char *, ...);
+ extern void lame_errorf(const lame_internal_flags * gfc, const char *, ...);
+ extern void lame_debugf(const lame_internal_flags * gfc, const char *, ...);
+ extern void lame_msgf(const lame_internal_flags * gfc, const char *, ...);
+#define DEBUGF lame_debugf
+#define ERRORF lame_errorf
+#define MSGF lame_msgf
+
+ int is_lame_internal_flags_valid(const lame_internal_flags * gfp);
+
+ extern void hip_set_pinfo(hip_t hip, plotting_data* pinfo);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LAME_UTIL_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/vbrquantize.c b/libnative/src/main/cpp/module/mp3/lame/vbrquantize.c
new file mode 100644
index 0000000000000000000000000000000000000000..e25e02d2703370d95a107c7e0d4f9ad3ba3b839f
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/vbrquantize.c
@@ -0,0 +1,1580 @@
+/*
+ * MP3 quantization
+ *
+ * Copyright (c) 1999-2000 Mark Taylor
+ * Copyright (c) 2000-2012 Robert Hegemann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: vbrquantize.c,v 1.141.2.1 2012/02/07 13:40:37 robert Exp $ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+#include "encoder.h"
+#include "util.h"
+#include "vbrquantize.h"
+#include "quantize_pvt.h"
+
+
+
+
+struct algo_s;
+typedef struct algo_s algo_t;
+
+typedef void (*alloc_sf_f) (const algo_t *, const int *, const int *, int);
+typedef uint8_t (*find_sf_f) (const FLOAT *, const FLOAT *, FLOAT, unsigned int, uint8_t);
+
+struct algo_s {
+ alloc_sf_f alloc;
+ find_sf_f find;
+ const FLOAT *xr34orig;
+ lame_internal_flags *gfc;
+ gr_info *cod_info;
+ int mingain_l;
+ int mingain_s[3];
+};
+
+
+
+/* Remarks on optimizing compilers:
+ *
+ * the MSVC compiler may get into aliasing problems when accessing
+ * memory through the fi_union. declaring it volatile does the trick here
+ *
+ * the calc_sfb_noise_* functions are not inlined because the intel compiler
+ * optimized executeables won't work as expected anymore
+ */
+
+#ifdef _MSC_VER
+# if _MSC_VER < 1400
+# define VOLATILE volatile
+# else
+# define VOLATILE
+# endif
+#else
+# define VOLATILE
+#endif
+
+typedef VOLATILE union {
+ float f;
+ int i;
+} fi_union;
+
+
+
+#ifdef TAKEHIRO_IEEE754_HACK
+#define DOUBLEX double
+#else
+#define DOUBLEX FLOAT
+#endif
+
+#define MAGIC_FLOAT_def (65536*(128))
+#define MAGIC_INT_def 0x4b000000
+
+#ifdef TAKEHIRO_IEEE754_HACK
+#else
+/*********************************************************************
+ * XRPOW_FTOI is a macro to convert floats to ints.
+ * if XRPOW_FTOI(x) = nearest_int(x), then QUANTFAC(x)=adj43asm[x]
+ * ROUNDFAC= -0.0946
+ *
+ * if XRPOW_FTOI(x) = floor(x), then QUANTFAC(x)=asj43[x]
+ * ROUNDFAC=0.4054
+ *********************************************************************/
+# define QUANTFAC(rx) adj43[rx]
+# define ROUNDFAC_def 0.4054f
+# define XRPOW_FTOI(src,dest) ((dest) = (int)(src))
+#endif
+
+static int const MAGIC_INT = MAGIC_INT_def;
+#ifndef TAKEHIRO_IEEE754_HACK
+static DOUBLEX const ROUNDFAC = ROUNDFAC_def;
+#endif
+static DOUBLEX const MAGIC_FLOAT = MAGIC_FLOAT_def;
+
+
+inline static float
+vec_max_c(const float * xr34, unsigned int bw)
+{
+ float xfsf = 0;
+ unsigned int i = bw >> 2u;
+ unsigned int const remaining = (bw & 0x03u);
+
+ while (i-- > 0) {
+ if (xfsf < xr34[0]) {
+ xfsf = xr34[0];
+ }
+ if (xfsf < xr34[1]) {
+ xfsf = xr34[1];
+ }
+ if (xfsf < xr34[2]) {
+ xfsf = xr34[2];
+ }
+ if (xfsf < xr34[3]) {
+ xfsf = xr34[3];
+ }
+ xr34 += 4;
+ }
+ switch( remaining ) {
+ case 3: if (xfsf < xr34[2]) xfsf = xr34[2];
+ case 2: if (xfsf < xr34[1]) xfsf = xr34[1];
+ case 1: if (xfsf < xr34[0]) xfsf = xr34[0];
+ default: break;
+ }
+ return xfsf;
+}
+
+
+inline static uint8_t
+find_lowest_scalefac(const FLOAT xr34)
+{
+ uint8_t sf_ok = 255;
+ uint8_t sf = 128, delsf = 64;
+ uint8_t i;
+ FLOAT const ixmax_val = IXMAX_VAL;
+ for (i = 0; i < 8; ++i) {
+ FLOAT const xfsf = ipow20[sf] * xr34;
+ if (xfsf <= ixmax_val) {
+ sf_ok = sf;
+ sf -= delsf;
+ }
+ else {
+ sf += delsf;
+ }
+ delsf >>= 1;
+ }
+ return sf_ok;
+}
+
+
+inline static void
+k_34_4(DOUBLEX x[4], int l3[4])
+{
+#ifdef TAKEHIRO_IEEE754_HACK
+ fi_union fi[4];
+
+ assert(x[0] <= IXMAX_VAL && x[1] <= IXMAX_VAL && x[2] <= IXMAX_VAL && x[3] <= IXMAX_VAL);
+ x[0] += MAGIC_FLOAT;
+ fi[0].f = x[0];
+ x[1] += MAGIC_FLOAT;
+ fi[1].f = x[1];
+ x[2] += MAGIC_FLOAT;
+ fi[2].f = x[2];
+ x[3] += MAGIC_FLOAT;
+ fi[3].f = x[3];
+ fi[0].f = x[0] + adj43asm[fi[0].i - MAGIC_INT];
+ fi[1].f = x[1] + adj43asm[fi[1].i - MAGIC_INT];
+ fi[2].f = x[2] + adj43asm[fi[2].i - MAGIC_INT];
+ fi[3].f = x[3] + adj43asm[fi[3].i - MAGIC_INT];
+ l3[0] = fi[0].i - MAGIC_INT;
+ l3[1] = fi[1].i - MAGIC_INT;
+ l3[2] = fi[2].i - MAGIC_INT;
+ l3[3] = fi[3].i - MAGIC_INT;
+#else
+ assert(x[0] <= IXMAX_VAL && x[1] <= IXMAX_VAL && x[2] <= IXMAX_VAL && x[3] <= IXMAX_VAL);
+ XRPOW_FTOI(x[0], l3[0]);
+ XRPOW_FTOI(x[1], l3[1]);
+ XRPOW_FTOI(x[2], l3[2]);
+ XRPOW_FTOI(x[3], l3[3]);
+ x[0] += QUANTFAC(l3[0]);
+ x[1] += QUANTFAC(l3[1]);
+ x[2] += QUANTFAC(l3[2]);
+ x[3] += QUANTFAC(l3[3]);
+ XRPOW_FTOI(x[0], l3[0]);
+ XRPOW_FTOI(x[1], l3[1]);
+ XRPOW_FTOI(x[2], l3[2]);
+ XRPOW_FTOI(x[3], l3[3]);
+#endif
+}
+
+
+
+
+
+/* do call the calc_sfb_noise_* functions only with sf values
+ * for which holds: sfpow34*xr34 <= IXMAX_VAL
+ */
+
+static FLOAT
+calc_sfb_noise_x34(const FLOAT * xr, const FLOAT * xr34, unsigned int bw, uint8_t sf)
+{
+ DOUBLEX x[4];
+ int l3[4];
+ const FLOAT sfpow = pow20[sf + Q_MAX2]; /*pow(2.0,sf/4.0); */
+ const FLOAT sfpow34 = ipow20[sf]; /*pow(sfpow,-3.0/4.0); */
+
+ FLOAT xfsf = 0;
+ unsigned int i = bw >> 2u;
+ unsigned int const remaining = (bw & 0x03u);
+
+ while (i-- > 0) {
+ x[0] = sfpow34 * xr34[0];
+ x[1] = sfpow34 * xr34[1];
+ x[2] = sfpow34 * xr34[2];
+ x[3] = sfpow34 * xr34[3];
+
+ k_34_4(x, l3);
+
+ x[0] = fabsf(xr[0]) - sfpow * pow43[l3[0]];
+ x[1] = fabsf(xr[1]) - sfpow * pow43[l3[1]];
+ x[2] = fabsf(xr[2]) - sfpow * pow43[l3[2]];
+ x[3] = fabsf(xr[3]) - sfpow * pow43[l3[3]];
+ xfsf += (x[0] * x[0] + x[1] * x[1]) + (x[2] * x[2] + x[3] * x[3]);
+
+ xr += 4;
+ xr34 += 4;
+ }
+ if (remaining) {
+ x[0] = x[1] = x[2] = x[3] = 0;
+ switch( remaining ) {
+ case 3: x[2] = sfpow34 * xr34[2];
+ case 2: x[1] = sfpow34 * xr34[1];
+ case 1: x[0] = sfpow34 * xr34[0];
+ }
+
+ k_34_4(x, l3);
+ x[0] = x[1] = x[2] = x[3] = 0;
+
+ switch( remaining ) {
+ case 3: x[2] = fabsf(xr[2]) - sfpow * pow43[l3[2]];
+ case 2: x[1] = fabsf(xr[1]) - sfpow * pow43[l3[1]];
+ case 1: x[0] = fabsf(xr[0]) - sfpow * pow43[l3[0]];
+ }
+ xfsf += (x[0] * x[0] + x[1] * x[1]) + (x[2] * x[2] + x[3] * x[3]);
+ }
+ return xfsf;
+}
+
+
+
+struct calc_noise_cache {
+ int valid;
+ FLOAT value;
+};
+
+typedef struct calc_noise_cache calc_noise_cache_t;
+
+
+static uint8_t
+tri_calc_sfb_noise_x34(const FLOAT * xr, const FLOAT * xr34, FLOAT l3_xmin, unsigned int bw,
+ uint8_t sf, calc_noise_cache_t * did_it)
+{
+ if (did_it[sf].valid == 0) {
+ did_it[sf].valid = 1;
+ did_it[sf].value = calc_sfb_noise_x34(xr, xr34, bw, sf);
+ }
+ if (l3_xmin < did_it[sf].value) {
+ return 1;
+ }
+ if (sf < 255) {
+ uint8_t const sf_x = sf + 1;
+ if (did_it[sf_x].valid == 0) {
+ did_it[sf_x].valid = 1;
+ did_it[sf_x].value = calc_sfb_noise_x34(xr, xr34, bw, sf_x);
+ }
+ if (l3_xmin < did_it[sf_x].value) {
+ return 1;
+ }
+ }
+ if (sf > 0) {
+ uint8_t const sf_x = sf - 1;
+ if (did_it[sf_x].valid == 0) {
+ did_it[sf_x].valid = 1;
+ did_it[sf_x].value = calc_sfb_noise_x34(xr, xr34, bw, sf_x);
+ }
+ if (l3_xmin < did_it[sf_x].value) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Robert Hegemann 2001-05-01
+ * calculates quantization step size determined by allowed masking
+ */
+static int
+calc_scalefac(FLOAT l3_xmin, int bw)
+{
+ FLOAT const c = 5.799142446; /* 10 * 10^(2/3) * log10(4/3) */
+ return 210 + (int) (c * log10f(l3_xmin / bw) - .5f);
+}
+
+static uint8_t
+guess_scalefac_x34(const FLOAT * xr, const FLOAT * xr34, FLOAT l3_xmin, unsigned int bw, uint8_t sf_min)
+{
+ int const guess = calc_scalefac(l3_xmin, bw);
+ if (guess < sf_min) return sf_min;
+ if (guess >= 255) return 255;
+ (void) xr;
+ (void) xr34;
+ return guess;
+}
+
+
+/* the find_scalefac* routines calculate
+ * a quantization step size which would
+ * introduce as much noise as is allowed.
+ * The larger the step size the more
+ * quantization noise we'll get. The
+ * scalefactors are there to lower the
+ * global step size, allowing limited
+ * differences in quantization step sizes
+ * per band (shaping the noise).
+ */
+
+static uint8_t
+find_scalefac_x34(const FLOAT * xr, const FLOAT * xr34, FLOAT l3_xmin, unsigned int bw,
+ uint8_t sf_min)
+{
+ calc_noise_cache_t did_it[256];
+ uint8_t sf = 128, sf_ok = 255, delsf = 128, seen_good_one = 0, i;
+ memset(did_it, 0, sizeof(did_it));
+ for (i = 0; i < 8; ++i) {
+ delsf >>= 1;
+ if (sf <= sf_min) {
+ sf += delsf;
+ }
+ else {
+ uint8_t const bad = tri_calc_sfb_noise_x34(xr, xr34, l3_xmin, bw, sf, did_it);
+ if (bad) { /* distortion. try a smaller scalefactor */
+ sf -= delsf;
+ }
+ else {
+ sf_ok = sf;
+ sf += delsf;
+ seen_good_one = 1;
+ }
+ }
+ }
+ /* returning a scalefac without distortion, if possible
+ */
+ if (seen_good_one > 0) {
+ sf = sf_ok;
+ }
+ if (sf <= sf_min) {
+ sf = sf_min;
+ }
+ return sf;
+}
+
+
+
+/***********************************************************************
+ *
+ * calc_short_block_vbr_sf()
+ * calc_long_block_vbr_sf()
+ *
+ * Mark Taylor 2000-??-??
+ * Robert Hegemann 2000-10-25 made functions of it
+ *
+ ***********************************************************************/
+
+/* a variation for vbr-mtrh */
+static int
+block_sf(algo_t * that, const FLOAT l3_xmin[SFBMAX], int vbrsf[SFBMAX], int vbrsfmin[SFBMAX])
+{
+ FLOAT max_xr34;
+ const FLOAT *const xr = &that->cod_info->xr[0];
+ const FLOAT *const xr34_orig = &that->xr34orig[0];
+ const int *const width = &that->cod_info->width[0];
+ const char *const energy_above_cutoff = &that->cod_info->energy_above_cutoff[0];
+ unsigned int const max_nonzero_coeff = (unsigned int) that->cod_info->max_nonzero_coeff;
+ uint8_t maxsf = 0;
+ int sfb = 0, m_o = -1;
+ unsigned int j = 0, i = 0;
+ int const psymax = that->cod_info->psymax;
+
+ assert(that->cod_info->max_nonzero_coeff >= 0);
+
+ that->mingain_l = 0;
+ that->mingain_s[0] = 0;
+ that->mingain_s[1] = 0;
+ that->mingain_s[2] = 0;
+ while (j <= max_nonzero_coeff) {
+ unsigned int const w = (unsigned int) width[sfb];
+ unsigned int const m = (unsigned int) (max_nonzero_coeff - j + 1);
+ unsigned int l = w;
+ uint8_t m1, m2;
+ if (l > m) {
+ l = m;
+ }
+ max_xr34 = vec_max_c(&xr34_orig[j], l);
+
+ m1 = find_lowest_scalefac(max_xr34);
+ vbrsfmin[sfb] = m1;
+ if (that->mingain_l < m1) {
+ that->mingain_l = m1;
+ }
+ if (that->mingain_s[i] < m1) {
+ that->mingain_s[i] = m1;
+ }
+ if (++i > 2) {
+ i = 0;
+ }
+ if (sfb < psymax && w > 2) { /* mpeg2.5 at 8 kHz doesn't use all scalefactors, unused have width 2 */
+ if (energy_above_cutoff[sfb]) {
+ m2 = that->find(&xr[j], &xr34_orig[j], l3_xmin[sfb], l, m1);
+#if 0
+ if (0) {
+ /** Robert Hegemann 2007-09-29:
+ * It seems here is some more potential for speed improvements.
+ * Current find method does 11-18 quantization calculations.
+ * Using a "good guess" may help to reduce this amount.
+ */
+ uint8_t guess = calc_scalefac(l3_xmin[sfb], l);
+ DEBUGF(that->gfc, "sfb=%3d guess=%3d found=%3d diff=%3d\n", sfb, guess, m2,
+ m2 - guess);
+ }
+#endif
+ if (maxsf < m2) {
+ maxsf = m2;
+ }
+ if (m_o < m2 && m2 < 255) {
+ m_o = m2;
+ }
+ }
+ else {
+ m2 = 255;
+ maxsf = 255;
+ }
+ }
+ else {
+ if (maxsf < m1) {
+ maxsf = m1;
+ }
+ m2 = maxsf;
+ }
+ vbrsf[sfb] = m2;
+ ++sfb;
+ j += w;
+ }
+ for (; sfb < SFBMAX; ++sfb) {
+ vbrsf[sfb] = maxsf;
+ vbrsfmin[sfb] = 0;
+ }
+ if (m_o > -1) {
+ maxsf = m_o;
+ for (sfb = 0; sfb < SFBMAX; ++sfb) {
+ if (vbrsf[sfb] == 255) {
+ vbrsf[sfb] = m_o;
+ }
+ }
+ }
+ return maxsf;
+}
+
+
+
+/***********************************************************************
+ *
+ * quantize xr34 based on scalefactors
+ *
+ * block_xr34
+ *
+ * Mark Taylor 2000-??-??
+ * Robert Hegemann 2000-10-20 made functions of them
+ *
+ ***********************************************************************/
+
+static void
+quantize_x34(const algo_t * that)
+{
+ DOUBLEX x[4];
+ const FLOAT *xr34_orig = that->xr34orig;
+ gr_info *const cod_info = that->cod_info;
+ int const ifqstep = (cod_info->scalefac_scale == 0) ? 2 : 4;
+ int *l3 = cod_info->l3_enc;
+ unsigned int j = 0, sfb = 0;
+ unsigned int const max_nonzero_coeff = (unsigned int) cod_info->max_nonzero_coeff;
+
+ assert(cod_info->max_nonzero_coeff >= 0);
+ assert(cod_info->max_nonzero_coeff < 576);
+
+ while (j <= max_nonzero_coeff) {
+ int const s =
+ (cod_info->scalefac[sfb] + (cod_info->preflag ? pretab[sfb] : 0)) * ifqstep
+ + cod_info->subblock_gain[cod_info->window[sfb]] * 8;
+ uint8_t const sfac = (uint8_t) (cod_info->global_gain - s);
+ FLOAT const sfpow34 = ipow20[sfac];
+ unsigned int const w = (unsigned int) cod_info->width[sfb];
+ unsigned int const m = (unsigned int) (max_nonzero_coeff - j + 1);
+ unsigned int i, remaining;
+
+ assert((cod_info->global_gain - s) >= 0);
+ assert(cod_info->width[sfb] >= 0);
+ j += w;
+ ++sfb;
+
+ i = (w <= m) ? w : m;
+ remaining = (i & 0x03u);
+ i >>= 2u;
+
+ while (i-- > 0) {
+ x[0] = sfpow34 * xr34_orig[0];
+ x[1] = sfpow34 * xr34_orig[1];
+ x[2] = sfpow34 * xr34_orig[2];
+ x[3] = sfpow34 * xr34_orig[3];
+
+ k_34_4(x, l3);
+
+ l3 += 4;
+ xr34_orig += 4;
+ }
+ if (remaining) {
+ int tmp_l3[4];
+ x[0] = x[1] = x[2] = x[3] = 0;
+ switch( remaining ) {
+ case 3: x[2] = sfpow34 * xr34_orig[2];
+ case 2: x[1] = sfpow34 * xr34_orig[1];
+ case 1: x[0] = sfpow34 * xr34_orig[0];
+ }
+
+ k_34_4(x, tmp_l3);
+
+ switch( remaining ) {
+ case 3: l3[2] = tmp_l3[2];
+ case 2: l3[1] = tmp_l3[1];
+ case 1: l3[0] = tmp_l3[0];
+ }
+
+ l3 += remaining;
+ xr34_orig += remaining;
+ }
+ }
+}
+
+
+
+static const uint8_t max_range_short[SBMAX_s * 3] = {
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 0, 0, 0
+};
+
+static const uint8_t max_range_long[SBMAX_l] = {
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0
+};
+
+static const uint8_t max_range_long_lsf_pretab[SBMAX_l] = {
+ 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+
+/*
+ sfb=0..5 scalefac < 16
+ sfb>5 scalefac < 8
+
+ ifqstep = ( cod_info->scalefac_scale == 0 ) ? 2 : 4;
+ ol_sf = (cod_info->global_gain-210.0);
+ ol_sf -= 8*cod_info->subblock_gain[i];
+ ol_sf -= ifqstep*scalefac[gr][ch].s[sfb][i];
+*/
+
+static void
+set_subblock_gain(gr_info * cod_info, const int mingain_s[3], int sf[])
+{
+ const int maxrange1 = 15, maxrange2 = 7;
+ const int ifqstepShift = (cod_info->scalefac_scale == 0) ? 1 : 2;
+ int *const sbg = cod_info->subblock_gain;
+ unsigned int const psymax = (unsigned int) cod_info->psymax;
+ unsigned int psydiv = 18;
+ int sbg0, sbg1, sbg2;
+ unsigned int sfb, i;
+ int min_sbg = 7;
+
+ if (psydiv > psymax) {
+ psydiv = psymax;
+ }
+ for (i = 0; i < 3; ++i) {
+ int maxsf1 = 0, maxsf2 = 0, minsf = 1000;
+ /* see if we should use subblock gain */
+ for (sfb = i; sfb < psydiv; sfb += 3) { /* part 1 */
+ int const v = -sf[sfb];
+ if (maxsf1 < v) {
+ maxsf1 = v;
+ }
+ if (minsf > v) {
+ minsf = v;
+ }
+ }
+ for (; sfb < SFBMAX; sfb += 3) { /* part 2 */
+ int const v = -sf[sfb];
+ if (maxsf2 < v) {
+ maxsf2 = v;
+ }
+ if (minsf > v) {
+ minsf = v;
+ }
+ }
+
+ /* boost subblock gain as little as possible so we can
+ * reach maxsf1 with scalefactors
+ * 8*sbg >= maxsf1
+ */
+ {
+ int const m1 = maxsf1 - (maxrange1 << ifqstepShift);
+ int const m2 = maxsf2 - (maxrange2 << ifqstepShift);
+
+ maxsf1 = Max(m1, m2);
+ }
+ if (minsf > 0) {
+ sbg[i] = minsf >> 3;
+ }
+ else {
+ sbg[i] = 0;
+ }
+ if (maxsf1 > 0) {
+ int const m1 = sbg[i];
+ int const m2 = (maxsf1 + 7) >> 3;
+ sbg[i] = Max(m1, m2);
+ }
+ if (sbg[i] > 0 && mingain_s[i] > (cod_info->global_gain - sbg[i] * 8)) {
+ sbg[i] = (cod_info->global_gain - mingain_s[i]) >> 3;
+ }
+ if (sbg[i] > 7) {
+ sbg[i] = 7;
+ }
+ if (min_sbg > sbg[i]) {
+ min_sbg = sbg[i];
+ }
+ }
+ sbg0 = sbg[0] * 8;
+ sbg1 = sbg[1] * 8;
+ sbg2 = sbg[2] * 8;
+ for (sfb = 0; sfb < SFBMAX; sfb += 3) {
+ sf[sfb + 0] += sbg0;
+ sf[sfb + 1] += sbg1;
+ sf[sfb + 2] += sbg2;
+ }
+ if (min_sbg > 0) {
+ for (i = 0; i < 3; ++i) {
+ sbg[i] -= min_sbg;
+ }
+ cod_info->global_gain -= min_sbg * 8;
+ }
+}
+
+
+
+/*
+ ifqstep = ( cod_info->scalefac_scale == 0 ) ? 2 : 4;
+ ol_sf = (cod_info->global_gain-210.0);
+ ol_sf -= ifqstep*scalefac[gr][ch].l[sfb];
+ if (cod_info->preflag && sfb>=11)
+ ol_sf -= ifqstep*pretab[sfb];
+*/
+static void
+set_scalefacs(gr_info * cod_info, const int *vbrsfmin, int sf[], const uint8_t * max_range)
+{
+ const int ifqstep = (cod_info->scalefac_scale == 0) ? 2 : 4;
+ const int ifqstepShift = (cod_info->scalefac_scale == 0) ? 1 : 2;
+ int *const scalefac = cod_info->scalefac;
+ int const sfbmax = cod_info->sfbmax;
+ int sfb;
+ int const *const sbg = cod_info->subblock_gain;
+ int const *const window = cod_info->window;
+ int const preflag = cod_info->preflag;
+
+ if (preflag) {
+ for (sfb = 11; sfb < sfbmax; ++sfb) {
+ sf[sfb] += pretab[sfb] * ifqstep;
+ }
+ }
+ for (sfb = 0; sfb < sfbmax; ++sfb) {
+ int const gain = cod_info->global_gain - (sbg[window[sfb]] * 8)
+ - ((preflag ? pretab[sfb] : 0) * ifqstep);
+
+ if (sf[sfb] < 0) {
+ int const m = gain - vbrsfmin[sfb];
+ /* ifqstep*scalefac >= -sf[sfb], so round UP */
+ scalefac[sfb] = (ifqstep - 1 - sf[sfb]) >> ifqstepShift;
+
+ if (scalefac[sfb] > max_range[sfb]) {
+ scalefac[sfb] = max_range[sfb];
+ }
+ if (scalefac[sfb] > 0 && (scalefac[sfb] << ifqstepShift) > m) {
+ scalefac[sfb] = m >> ifqstepShift;
+ }
+ }
+ else {
+ scalefac[sfb] = 0;
+ }
+ }
+ for (; sfb < SFBMAX; ++sfb) {
+ scalefac[sfb] = 0; /* sfb21 */
+ }
+}
+
+
+#ifndef NDEBUG
+static int
+checkScalefactor(const gr_info * cod_info, const int vbrsfmin[SFBMAX])
+{
+ int const ifqstep = cod_info->scalefac_scale == 0 ? 2 : 4;
+ int sfb;
+ for (sfb = 0; sfb < cod_info->psymax; ++sfb) {
+ const int s =
+ ((cod_info->scalefac[sfb] +
+ (cod_info->preflag ? pretab[sfb] : 0)) * ifqstep) +
+ cod_info->subblock_gain[cod_info->window[sfb]] * 8;
+
+ if ((cod_info->global_gain - s) < vbrsfmin[sfb]) {
+ /*
+ fprintf( stdout, "sf %d\n", sfb );
+ fprintf( stdout, "min %d\n", vbrsfmin[sfb] );
+ fprintf( stdout, "ggain %d\n", cod_info->global_gain );
+ fprintf( stdout, "scalefac %d\n", cod_info->scalefac[sfb] );
+ fprintf( stdout, "pretab %d\n", (cod_info->preflag ? pretab[sfb] : 0) );
+ fprintf( stdout, "scale %d\n", (cod_info->scalefac_scale + 1) );
+ fprintf( stdout, "subgain %d\n", cod_info->subblock_gain[cod_info->window[sfb]] * 8 );
+ fflush( stdout );
+ exit(-1);
+ */
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+
+/******************************************************************
+ *
+ * short block scalefacs
+ *
+ ******************************************************************/
+
+static void
+short_block_constrain(const algo_t * that, const int vbrsf[SFBMAX],
+ const int vbrsfmin[SFBMAX], int vbrmax)
+{
+ gr_info *const cod_info = that->cod_info;
+ lame_internal_flags const *const gfc = that->gfc;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int const maxminsfb = that->mingain_l;
+ int mover, maxover0 = 0, maxover1 = 0, delta = 0;
+ int v, v0, v1;
+ int sfb;
+ int const psymax = cod_info->psymax;
+
+ for (sfb = 0; sfb < psymax; ++sfb) {
+ assert(vbrsf[sfb] >= vbrsfmin[sfb]);
+ v = vbrmax - vbrsf[sfb];
+ if (delta < v) {
+ delta = v;
+ }
+ v0 = v - (4 * 14 + 2 * max_range_short[sfb]);
+ v1 = v - (4 * 14 + 4 * max_range_short[sfb]);
+ if (maxover0 < v0) {
+ maxover0 = v0;
+ }
+ if (maxover1 < v1) {
+ maxover1 = v1;
+ }
+ }
+ if (cfg->noise_shaping == 2) {
+ /* allow scalefac_scale=1 */
+ mover = Min(maxover0, maxover1);
+ }
+ else {
+ mover = maxover0;
+ }
+ if (delta > mover) {
+ delta = mover;
+ }
+ vbrmax -= delta;
+ maxover0 -= mover;
+ maxover1 -= mover;
+
+ if (maxover0 == 0) {
+ cod_info->scalefac_scale = 0;
+ }
+ else if (maxover1 == 0) {
+ cod_info->scalefac_scale = 1;
+ }
+ if (vbrmax < maxminsfb) {
+ vbrmax = maxminsfb;
+ }
+ cod_info->global_gain = vbrmax;
+
+ if (cod_info->global_gain < 0) {
+ cod_info->global_gain = 0;
+ }
+ else if (cod_info->global_gain > 255) {
+ cod_info->global_gain = 255;
+ }
+ {
+ int sf_temp[SFBMAX];
+ for (sfb = 0; sfb < SFBMAX; ++sfb) {
+ sf_temp[sfb] = vbrsf[sfb] - vbrmax;
+ }
+ set_subblock_gain(cod_info, &that->mingain_s[0], sf_temp);
+ set_scalefacs(cod_info, vbrsfmin, sf_temp, max_range_short);
+ }
+ assert(checkScalefactor(cod_info, vbrsfmin));
+}
+
+
+
+/******************************************************************
+ *
+ * long block scalefacs
+ *
+ ******************************************************************/
+
+static void
+long_block_constrain(const algo_t * that, const int vbrsf[SFBMAX], const int vbrsfmin[SFBMAX],
+ int vbrmax)
+{
+ gr_info *const cod_info = that->cod_info;
+ lame_internal_flags const *const gfc = that->gfc;
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ uint8_t const *max_rangep;
+ int const maxminsfb = that->mingain_l;
+ int sfb;
+ int maxover0, maxover1, maxover0p, maxover1p, mover, delta = 0;
+ int v, v0, v1, v0p, v1p, vm0p = 1, vm1p = 1;
+ int const psymax = cod_info->psymax;
+
+ max_rangep = cfg->mode_gr == 2 ? max_range_long : max_range_long_lsf_pretab;
+
+ maxover0 = 0;
+ maxover1 = 0;
+ maxover0p = 0; /* pretab */
+ maxover1p = 0; /* pretab */
+
+ for (sfb = 0; sfb < psymax; ++sfb) {
+ assert(vbrsf[sfb] >= vbrsfmin[sfb]);
+ v = vbrmax - vbrsf[sfb];
+ if (delta < v) {
+ delta = v;
+ }
+ v0 = v - 2 * max_range_long[sfb];
+ v1 = v - 4 * max_range_long[sfb];
+ v0p = v - 2 * (max_rangep[sfb] + pretab[sfb]);
+ v1p = v - 4 * (max_rangep[sfb] + pretab[sfb]);
+ if (maxover0 < v0) {
+ maxover0 = v0;
+ }
+ if (maxover1 < v1) {
+ maxover1 = v1;
+ }
+ if (maxover0p < v0p) {
+ maxover0p = v0p;
+ }
+ if (maxover1p < v1p) {
+ maxover1p = v1p;
+ }
+ }
+ if (vm0p == 1) {
+ int gain = vbrmax - maxover0p;
+ if (gain < maxminsfb) {
+ gain = maxminsfb;
+ }
+ for (sfb = 0; sfb < psymax; ++sfb) {
+ int const a = (gain - vbrsfmin[sfb]) - 2 * pretab[sfb];
+ if (a <= 0) {
+ vm0p = 0;
+ vm1p = 0;
+ break;
+ }
+ }
+ }
+ if (vm1p == 1) {
+ int gain = vbrmax - maxover1p;
+ if (gain < maxminsfb) {
+ gain = maxminsfb;
+ }
+ for (sfb = 0; sfb < psymax; ++sfb) {
+ int const b = (gain - vbrsfmin[sfb]) - 4 * pretab[sfb];
+ if (b <= 0) {
+ vm1p = 0;
+ break;
+ }
+ }
+ }
+ if (vm0p == 0) {
+ maxover0p = maxover0;
+ }
+ if (vm1p == 0) {
+ maxover1p = maxover1;
+ }
+ if (cfg->noise_shaping != 2) {
+ maxover1 = maxover0;
+ maxover1p = maxover0p;
+ }
+ mover = Min(maxover0, maxover0p);
+ mover = Min(mover, maxover1);
+ mover = Min(mover, maxover1p);
+
+ if (delta > mover) {
+ delta = mover;
+ }
+ vbrmax -= delta;
+ if (vbrmax < maxminsfb) {
+ vbrmax = maxminsfb;
+ }
+ maxover0 -= mover;
+ maxover0p -= mover;
+ maxover1 -= mover;
+ maxover1p -= mover;
+
+ if (maxover0 == 0) {
+ cod_info->scalefac_scale = 0;
+ cod_info->preflag = 0;
+ max_rangep = max_range_long;
+ }
+ else if (maxover0p == 0) {
+ cod_info->scalefac_scale = 0;
+ cod_info->preflag = 1;
+ }
+ else if (maxover1 == 0) {
+ cod_info->scalefac_scale = 1;
+ cod_info->preflag = 0;
+ max_rangep = max_range_long;
+ }
+ else if (maxover1p == 0) {
+ cod_info->scalefac_scale = 1;
+ cod_info->preflag = 1;
+ }
+ else {
+ assert(0); /* this should not happen */
+ }
+ cod_info->global_gain = vbrmax;
+ if (cod_info->global_gain < 0) {
+ cod_info->global_gain = 0;
+ }
+ else if (cod_info->global_gain > 255) {
+ cod_info->global_gain = 255;
+ }
+ {
+ int sf_temp[SFBMAX];
+ for (sfb = 0; sfb < SFBMAX; ++sfb) {
+ sf_temp[sfb] = vbrsf[sfb] - vbrmax;
+ }
+ set_scalefacs(cod_info, vbrsfmin, sf_temp, max_rangep);
+ }
+ assert(checkScalefactor(cod_info, vbrsfmin));
+}
+
+
+
+static void
+bitcount(const algo_t * that)
+{
+ int rc = scale_bitcount(that->gfc, that->cod_info);
+
+ if (rc == 0) {
+ return;
+ }
+ /* this should not happen due to the way the scalefactors are selected */
+ ERRORF(that->gfc, "INTERNAL ERROR IN VBR NEW CODE (986), please send bug report\n");
+ exit(-1);
+}
+
+
+
+static int
+quantizeAndCountBits(const algo_t * that)
+{
+ quantize_x34(that);
+ that->cod_info->part2_3_length = noquant_count_bits(that->gfc, that->cod_info, 0);
+ return that->cod_info->part2_3_length;
+}
+
+
+
+
+
+static int
+tryGlobalStepsize(const algo_t * that, const int sfwork[SFBMAX],
+ const int vbrsfmin[SFBMAX], int delta)
+{
+ FLOAT const xrpow_max = that->cod_info->xrpow_max;
+ int sftemp[SFBMAX], i, nbits;
+ int gain, vbrmax = 0;
+ for (i = 0; i < SFBMAX; ++i) {
+ gain = sfwork[i] + delta;
+ if (gain < vbrsfmin[i]) {
+ gain = vbrsfmin[i];
+ }
+ if (gain > 255) {
+ gain = 255;
+ }
+ if (vbrmax < gain) {
+ vbrmax = gain;
+ }
+ sftemp[i] = gain;
+ }
+ that->alloc(that, sftemp, vbrsfmin, vbrmax);
+ bitcount(that);
+ nbits = quantizeAndCountBits(that);
+ that->cod_info->xrpow_max = xrpow_max;
+ return nbits;
+}
+
+
+
+static void
+searchGlobalStepsizeMax(const algo_t * that, const int sfwork[SFBMAX],
+ const int vbrsfmin[SFBMAX], int target)
+{
+ gr_info const *const cod_info = that->cod_info;
+ const int gain = cod_info->global_gain;
+ int curr = gain;
+ int gain_ok = 1024;
+ int nbits = LARGE_BITS;
+ int l = gain, r = 512;
+
+ assert(gain >= 0);
+ while (l <= r) {
+ curr = (l + r) >> 1;
+ nbits = tryGlobalStepsize(that, sfwork, vbrsfmin, curr - gain);
+ if (nbits == 0 || (nbits + cod_info->part2_length) < target) {
+ r = curr - 1;
+ gain_ok = curr;
+ }
+ else {
+ l = curr + 1;
+ if (gain_ok == 1024) {
+ gain_ok = curr;
+ }
+ }
+ }
+ if (gain_ok != curr) {
+ curr = gain_ok;
+ nbits = tryGlobalStepsize(that, sfwork, vbrsfmin, curr - gain);
+ }
+}
+
+
+
+static int
+sfDepth(const int sfwork[SFBMAX])
+{
+ int m = 0;
+ unsigned int i, j;
+ for (j = SFBMAX, i = 0; j > 0; --j, ++i) {
+ int const di = 255 - sfwork[i];
+ if (m < di) {
+ m = di;
+ }
+ assert(sfwork[i] >= 0);
+ assert(sfwork[i] <= 255);
+ }
+ assert(m >= 0);
+ assert(m <= 255);
+ return m;
+}
+
+
+static void
+cutDistribution(const int sfwork[SFBMAX], int sf_out[SFBMAX], int cut)
+{
+ unsigned int i, j;
+ for (j = SFBMAX, i = 0; j > 0; --j, ++i) {
+ int const x = sfwork[i];
+ sf_out[i] = x < cut ? x : cut;
+ }
+}
+
+
+static int
+flattenDistribution(const int sfwork[SFBMAX], int sf_out[SFBMAX], int dm, int k, int p)
+{
+ unsigned int i, j;
+ int x, sfmax = 0;
+ if (dm > 0) {
+ for (j = SFBMAX, i = 0; j > 0; --j, ++i) {
+ int const di = p - sfwork[i];
+ x = sfwork[i] + (k * di) / dm;
+ if (x < 0) {
+ x = 0;
+ }
+ else {
+ if (x > 255) {
+ x = 255;
+ }
+ }
+ sf_out[i] = x;
+ if (sfmax < x) {
+ sfmax = x;
+ }
+ }
+ }
+ else {
+ for (j = SFBMAX, i = 0; j > 0u; --j, ++i) {
+ x = sfwork[i];
+ sf_out[i] = x;
+ if (sfmax < x) {
+ sfmax = x;
+ }
+ }
+ }
+ return sfmax;
+}
+
+
+static int
+tryThatOne(algo_t const* that, const int sftemp[SFBMAX], const int vbrsfmin[SFBMAX], int vbrmax)
+{
+ FLOAT const xrpow_max = that->cod_info->xrpow_max;
+ int nbits = LARGE_BITS;
+ that->alloc(that, sftemp, vbrsfmin, vbrmax);
+ bitcount(that);
+ nbits = quantizeAndCountBits(that);
+ nbits += that->cod_info->part2_length;
+ that->cod_info->xrpow_max = xrpow_max;
+ return nbits;
+}
+
+
+static void
+outOfBitsStrategy(algo_t const* that, const int sfwork[SFBMAX], const int vbrsfmin[SFBMAX], int target)
+{
+ int wrk[SFBMAX];
+ int const dm = sfDepth(sfwork);
+ int const p = that->cod_info->global_gain;
+ int nbits;
+
+ /* PART 1 */
+ {
+ int bi = dm / 2;
+ int bi_ok = -1;
+ int bu = 0;
+ int bo = dm;
+ for (;;) {
+ int const sfmax = flattenDistribution(sfwork, wrk, dm, bi, p);
+ nbits = tryThatOne(that, wrk, vbrsfmin, sfmax);
+ if (nbits <= target) {
+ bi_ok = bi;
+ bo = bi - 1;
+ }
+ else {
+ bu = bi + 1;
+ }
+ if (bu <= bo) {
+ bi = (bu + bo) / 2;
+ }
+ else {
+ break;
+ }
+ }
+ if (bi_ok >= 0) {
+ if (bi != bi_ok) {
+ int const sfmax = flattenDistribution(sfwork, wrk, dm, bi_ok, p);
+ nbits = tryThatOne(that, wrk, vbrsfmin, sfmax);
+ }
+ return;
+ }
+ }
+
+ /* PART 2: */
+ {
+ int bi = (255 + p) / 2;
+ int bi_ok = -1;
+ int bu = p;
+ int bo = 255;
+ for (;;) {
+ int const sfmax = flattenDistribution(sfwork, wrk, dm, dm, bi);
+ nbits = tryThatOne(that, wrk, vbrsfmin, sfmax);
+ if (nbits <= target) {
+ bi_ok = bi;
+ bo = bi - 1;
+ }
+ else {
+ bu = bi + 1;
+ }
+ if (bu <= bo) {
+ bi = (bu + bo) / 2;
+ }
+ else {
+ break;
+ }
+ }
+ if (bi_ok >= 0) {
+ if (bi != bi_ok) {
+ int const sfmax = flattenDistribution(sfwork, wrk, dm, dm, bi_ok);
+ nbits = tryThatOne(that, wrk, vbrsfmin, sfmax);
+ }
+ return;
+ }
+ }
+
+ /* fall back to old code, likely to be never called */
+ searchGlobalStepsizeMax(that, wrk, vbrsfmin, target);
+}
+
+
+static int
+reduce_bit_usage(lame_internal_flags * gfc, int gr, int ch
+#if 0
+ , const FLOAT xr34orig[576], const FLOAT l3_xmin[SFBMAX], int maxbits
+#endif
+ )
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ gr_info *const cod_info = &gfc->l3_side.tt[gr][ch];
+ /* try some better scalefac storage
+ */
+ best_scalefac_store(gfc, gr, ch, &gfc->l3_side);
+
+ /* best huffman_divide may save some bits too
+ */
+ if (cfg->use_best_huffman == 1)
+ best_huffman_divide(gfc, cod_info);
+ return cod_info->part2_3_length + cod_info->part2_length;
+}
+
+
+
+
+int
+VBR_encode_frame(lame_internal_flags * gfc, const FLOAT xr34orig[2][2][576],
+ const FLOAT l3_xmin[2][2][SFBMAX], const int max_bits[2][2])
+{
+ SessionConfig_t const *const cfg = &gfc->cfg;
+ int sfwork_[2][2][SFBMAX];
+ int vbrsfmin_[2][2][SFBMAX];
+ algo_t that_[2][2];
+ int const ngr = cfg->mode_gr;
+ int const nch = cfg->channels_out;
+ int max_nbits_ch[2][2] = {{0, 0}, {0 ,0}};
+ int max_nbits_gr[2] = {0, 0};
+ int max_nbits_fr = 0;
+ int use_nbits_ch[2][2] = {{MAX_BITS_PER_CHANNEL+1, MAX_BITS_PER_CHANNEL+1}
+ ,{MAX_BITS_PER_CHANNEL+1, MAX_BITS_PER_CHANNEL+1}};
+ int use_nbits_gr[2] = { MAX_BITS_PER_GRANULE+1, MAX_BITS_PER_GRANULE+1 };
+ int use_nbits_fr = MAX_BITS_PER_GRANULE+MAX_BITS_PER_GRANULE;
+ int gr, ch;
+ int ok, sum_fr;
+
+ /* set up some encoding parameters
+ */
+ for (gr = 0; gr < ngr; ++gr) {
+ max_nbits_gr[gr] = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ max_nbits_ch[gr][ch] = max_bits[gr][ch];
+ use_nbits_ch[gr][ch] = 0;
+ max_nbits_gr[gr] += max_bits[gr][ch];
+ max_nbits_fr += max_bits[gr][ch];
+ that_[gr][ch].find = (cfg->full_outer_loop < 0) ? guess_scalefac_x34 : find_scalefac_x34;
+ that_[gr][ch].gfc = gfc;
+ that_[gr][ch].cod_info = &gfc->l3_side.tt[gr][ch];
+ that_[gr][ch].xr34orig = xr34orig[gr][ch];
+ if (that_[gr][ch].cod_info->block_type == SHORT_TYPE) {
+ that_[gr][ch].alloc = short_block_constrain;
+ }
+ else {
+ that_[gr][ch].alloc = long_block_constrain;
+ }
+ } /* for ch */
+ }
+ /* searches scalefactors
+ */
+ for (gr = 0; gr < ngr; ++gr) {
+ for (ch = 0; ch < nch; ++ch) {
+ if (max_bits[gr][ch] > 0) {
+ algo_t *that = &that_[gr][ch];
+ int *sfwork = sfwork_[gr][ch];
+ int *vbrsfmin = vbrsfmin_[gr][ch];
+ int vbrmax;
+
+ vbrmax = block_sf(that, l3_xmin[gr][ch], sfwork, vbrsfmin);
+ that->alloc(that, sfwork, vbrsfmin, vbrmax);
+ bitcount(that);
+ }
+ else {
+ /* xr contains no energy
+ * l3_enc, our encoding data, will be quantized to zero
+ * continue with next channel
+ */
+ }
+ } /* for ch */
+ }
+ /* encode 'as is'
+ */
+ use_nbits_fr = 0;
+ for (gr = 0; gr < ngr; ++gr) {
+ use_nbits_gr[gr] = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ algo_t const *that = &that_[gr][ch];
+ if (max_bits[gr][ch] > 0) {
+ memset(&that->cod_info->l3_enc[0], 0, sizeof(that->cod_info->l3_enc));
+ (void) quantizeAndCountBits(that);
+ }
+ else {
+ /* xr contains no energy
+ * l3_enc, our encoding data, will be quantized to zero
+ * continue with next channel
+ */
+ }
+ use_nbits_ch[gr][ch] = reduce_bit_usage(gfc, gr, ch);
+ use_nbits_gr[gr] += use_nbits_ch[gr][ch];
+ } /* for ch */
+ use_nbits_fr += use_nbits_gr[gr];
+ }
+
+ /* check bit constrains
+ */
+ if (use_nbits_fr <= max_nbits_fr) {
+ ok = 1;
+ for (gr = 0; gr < ngr; ++gr) {
+ if (use_nbits_gr[gr] > MAX_BITS_PER_GRANULE) {
+ /* violates the rule that every granule has to use no more
+ * bits than MAX_BITS_PER_GRANULE
+ */
+ ok = 0;
+ }
+ for (ch = 0; ch < nch; ++ch) {
+ if (use_nbits_ch[gr][ch] > MAX_BITS_PER_CHANNEL) {
+ /* violates the rule that every gr_ch has to use no more
+ * bits than MAX_BITS_PER_CHANNEL
+ *
+ * This isn't explicitly stated in the ISO docs, but the
+ * part2_3_length field has only 12 bits, that makes it
+ * up to a maximum size of 4095 bits!!!
+ */
+ ok = 0;
+ }
+ }
+ }
+ if (ok) {
+ return use_nbits_fr;
+ }
+ }
+
+ /* OK, we are in trouble and have to define how many bits are
+ * to be used for each granule
+ */
+ {
+ ok = 1;
+ sum_fr = 0;
+
+ for (gr = 0; gr < ngr; ++gr) {
+ max_nbits_gr[gr] = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ if (use_nbits_ch[gr][ch] > MAX_BITS_PER_CHANNEL) {
+ max_nbits_ch[gr][ch] = MAX_BITS_PER_CHANNEL;
+ }
+ else {
+ max_nbits_ch[gr][ch] = use_nbits_ch[gr][ch];
+ }
+ max_nbits_gr[gr] += max_nbits_ch[gr][ch];
+ }
+ if (max_nbits_gr[gr] > MAX_BITS_PER_GRANULE) {
+ float f[2] = {0.0f, 0.0f}, s = 0.0f;
+ for (ch = 0; ch < nch; ++ch) {
+ if (max_nbits_ch[gr][ch] > 0) {
+ f[ch] = sqrt(sqrt(max_nbits_ch[gr][ch]));
+ s += f[ch];
+ }
+ else {
+ f[ch] = 0;
+ }
+ }
+ for (ch = 0; ch < nch; ++ch) {
+ if (s > 0) {
+ max_nbits_ch[gr][ch] = MAX_BITS_PER_GRANULE * f[ch] / s;
+ }
+ else {
+ max_nbits_ch[gr][ch] = 0;
+ }
+ }
+ if (nch > 1) {
+ if (max_nbits_ch[gr][0] > use_nbits_ch[gr][0] + 32) {
+ max_nbits_ch[gr][1] += max_nbits_ch[gr][0];
+ max_nbits_ch[gr][1] -= use_nbits_ch[gr][0] + 32;
+ max_nbits_ch[gr][0] = use_nbits_ch[gr][0] + 32;
+ }
+ if (max_nbits_ch[gr][1] > use_nbits_ch[gr][1] + 32) {
+ max_nbits_ch[gr][0] += max_nbits_ch[gr][1];
+ max_nbits_ch[gr][0] -= use_nbits_ch[gr][1] + 32;
+ max_nbits_ch[gr][1] = use_nbits_ch[gr][1] + 32;
+ }
+ if (max_nbits_ch[gr][0] > MAX_BITS_PER_CHANNEL) {
+ max_nbits_ch[gr][0] = MAX_BITS_PER_CHANNEL;
+ }
+ if (max_nbits_ch[gr][1] > MAX_BITS_PER_CHANNEL) {
+ max_nbits_ch[gr][1] = MAX_BITS_PER_CHANNEL;
+ }
+ }
+ max_nbits_gr[gr] = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ max_nbits_gr[gr] += max_nbits_ch[gr][ch];
+ }
+ }
+ sum_fr += max_nbits_gr[gr];
+ }
+ if (sum_fr > max_nbits_fr) {
+ {
+ float f[2] = {0.0f, 0.0f}, s = 0.0f;
+ for (gr = 0; gr < ngr; ++gr) {
+ if (max_nbits_gr[gr] > 0) {
+ f[gr] = sqrt(max_nbits_gr[gr]);
+ s += f[gr];
+ }
+ else {
+ f[gr] = 0;
+ }
+ }
+ for (gr = 0; gr < ngr; ++gr) {
+ if (s > 0) {
+ max_nbits_gr[gr] = max_nbits_fr * f[gr] / s;
+ }
+ else {
+ max_nbits_gr[gr] = 0;
+ }
+ }
+ }
+ if (ngr > 1) {
+ if (max_nbits_gr[0] > use_nbits_gr[0] + 125) {
+ max_nbits_gr[1] += max_nbits_gr[0];
+ max_nbits_gr[1] -= use_nbits_gr[0] + 125;
+ max_nbits_gr[0] = use_nbits_gr[0] + 125;
+ }
+ if (max_nbits_gr[1] > use_nbits_gr[1] + 125) {
+ max_nbits_gr[0] += max_nbits_gr[1];
+ max_nbits_gr[0] -= use_nbits_gr[1] + 125;
+ max_nbits_gr[1] = use_nbits_gr[1] + 125;
+ }
+ for (gr = 0; gr < ngr; ++gr) {
+ if (max_nbits_gr[gr] > MAX_BITS_PER_GRANULE) {
+ max_nbits_gr[gr] = MAX_BITS_PER_GRANULE;
+ }
+ }
+ }
+ for (gr = 0; gr < ngr; ++gr) {
+ float f[2] = {0.0f, 0.0f}, s = 0.0f;
+ for (ch = 0; ch < nch; ++ch) {
+ if (max_nbits_ch[gr][ch] > 0) {
+ f[ch] = sqrt(max_nbits_ch[gr][ch]);
+ s += f[ch];
+ }
+ else {
+ f[ch] = 0;
+ }
+ }
+ for (ch = 0; ch < nch; ++ch) {
+ if (s > 0) {
+ max_nbits_ch[gr][ch] = max_nbits_gr[gr] * f[ch] / s;
+ }
+ else {
+ max_nbits_ch[gr][ch] = 0;
+ }
+ }
+ if (nch > 1) {
+ if (max_nbits_ch[gr][0] > use_nbits_ch[gr][0] + 32) {
+ max_nbits_ch[gr][1] += max_nbits_ch[gr][0];
+ max_nbits_ch[gr][1] -= use_nbits_ch[gr][0] + 32;
+ max_nbits_ch[gr][0] = use_nbits_ch[gr][0] + 32;
+ }
+ if (max_nbits_ch[gr][1] > use_nbits_ch[gr][1] + 32) {
+ max_nbits_ch[gr][0] += max_nbits_ch[gr][1];
+ max_nbits_ch[gr][0] -= use_nbits_ch[gr][1] + 32;
+ max_nbits_ch[gr][1] = use_nbits_ch[gr][1] + 32;
+ }
+ for (ch = 0; ch < nch; ++ch) {
+ if (max_nbits_ch[gr][ch] > MAX_BITS_PER_CHANNEL) {
+ max_nbits_ch[gr][ch] = MAX_BITS_PER_CHANNEL;
+ }
+ }
+ }
+ }
+ }
+ /* sanity check */
+ sum_fr = 0;
+ for (gr = 0; gr < ngr; ++gr) {
+ int sum_gr = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ sum_gr += max_nbits_ch[gr][ch];
+ if (max_nbits_ch[gr][ch] > MAX_BITS_PER_CHANNEL) {
+ ok = 0;
+ }
+ }
+ sum_fr += sum_gr;
+ if (sum_gr > MAX_BITS_PER_GRANULE) {
+ ok = 0;
+ }
+ }
+ if (sum_fr > max_nbits_fr) {
+ ok = 0;
+ }
+ if (!ok) {
+ /* we must have done something wrong, fallback to 'on_pe' based constrain */
+ for (gr = 0; gr < ngr; ++gr) {
+ for (ch = 0; ch < nch; ++ch) {
+ max_nbits_ch[gr][ch] = max_bits[gr][ch];
+ }
+ }
+ }
+ }
+
+ /* we already called the 'best_scalefac_store' function, so we need to reset some
+ * variables before we can do it again.
+ */
+ for (ch = 0; ch < nch; ++ch) {
+ gfc->l3_side.scfsi[ch][0] = 0;
+ gfc->l3_side.scfsi[ch][1] = 0;
+ gfc->l3_side.scfsi[ch][2] = 0;
+ gfc->l3_side.scfsi[ch][3] = 0;
+ }
+ for (gr = 0; gr < ngr; ++gr) {
+ for (ch = 0; ch < nch; ++ch) {
+ gfc->l3_side.tt[gr][ch].scalefac_compress = 0;
+ }
+ }
+
+ /* alter our encoded data, until it fits into the target bitrate
+ */
+ use_nbits_fr = 0;
+ for (gr = 0; gr < ngr; ++gr) {
+ use_nbits_gr[gr] = 0;
+ for (ch = 0; ch < nch; ++ch) {
+ algo_t const *that = &that_[gr][ch];
+ use_nbits_ch[gr][ch] = 0;
+ if (max_bits[gr][ch] > 0) {
+ int *sfwork = sfwork_[gr][ch];
+ int const *vbrsfmin = vbrsfmin_[gr][ch];
+ cutDistribution(sfwork, sfwork, that->cod_info->global_gain);
+ outOfBitsStrategy(that, sfwork, vbrsfmin, max_nbits_ch[gr][ch]);
+ }
+ use_nbits_ch[gr][ch] = reduce_bit_usage(gfc, gr, ch);
+ assert(use_nbits_ch[gr][ch] <= max_nbits_ch[gr][ch]);
+ use_nbits_gr[gr] += use_nbits_ch[gr][ch];
+ } /* for ch */
+ use_nbits_fr += use_nbits_gr[gr];
+ }
+
+ /* check bit constrains, but it should always be ok, iff there are no bugs ;-)
+ */
+ if (use_nbits_fr <= max_nbits_fr) {
+ return use_nbits_fr;
+ }
+
+ ERRORF(gfc, "INTERNAL ERROR IN VBR NEW CODE (1313), please send bug report\n"
+ "maxbits=%d usedbits=%d\n", max_nbits_fr, use_nbits_fr);
+ exit(-1);
+}
diff --git a/libnative/src/main/cpp/module/mp3/lame/vbrquantize.h b/libnative/src/main/cpp/module/mp3/lame/vbrquantize.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c0d18f0775a77dc62c5755a0ca0344a14f2a692
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/vbrquantize.h
@@ -0,0 +1,28 @@
+/*
+ * MP3 VBR quantization
+ *
+ * Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_VBRQUANTIZE_H
+#define LAME_VBRQUANTIZE_H
+
+int VBR_encode_frame(lame_internal_flags * gfc, const FLOAT xr34orig[2][2][576],
+ const FLOAT l3_xmin[2][2][SFBMAX], const int maxbits[2][2]);
+
+#endif /* LAME_VBRQUANTIZE_H */
diff --git a/libnative/src/main/cpp/module/mp3/lame/version.c b/libnative/src/main/cpp/module/mp3/lame/version.c
new file mode 100644
index 0000000000000000000000000000000000000000..82058125b60de172d128a38df8e3dd64ece54136
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/version.c
@@ -0,0 +1,254 @@
+/*
+ * Version numbering for LAME.
+ *
+ * Copyright (c) 1999 A.L. Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*!
+ \file version.c
+ \brief Version numbering for LAME.
+
+ Contains functions which describe the version of LAME.
+
+ \author A.L. Faber
+ \version \$Id: version.c,v 1.32.2.2 2011/11/18 09:18:28 robert Exp $
+ \ingroup libmp3lame
+*/
+
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+
+#include "lame.h"
+#include "machine.h"
+
+#include "version.h" /* macros of version numbers */
+
+
+
+
+
+/*! Get the LAME version string. */
+/*!
+ \param void
+ \return a pointer to a string which describes the version of LAME.
+*/
+const char *
+get_lame_version(void)
+{ /* primary to write screen reports */
+ /* Here we can also add informations about compile time configurations */
+
+#if LAME_ALPHA_VERSION
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) " "
+ "(alpha " STR(LAME_PATCH_VERSION) ", " __DATE__ " " __TIME__ ")";
+#elif LAME_BETA_VERSION
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) " "
+ "(beta " STR(LAME_PATCH_VERSION) ", " __DATE__ ")";
+#elif LAME_RELEASE_VERSION && (LAME_PATCH_VERSION > 0)
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) "." STR(LAME_PATCH_VERSION);
+#else
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION);
+#endif
+
+ return str;
+}
+
+
+/*! Get the short LAME version string. */
+/*!
+ It's mainly for inclusion into the MP3 stream.
+
+ \param void
+ \return a pointer to the short version of the LAME version string.
+*/
+const char *
+get_lame_short_version(void)
+{
+ /* adding date and time to version string makes it harder for output
+ validation */
+
+#if LAME_ALPHA_VERSION
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) " (alpha " STR(LAME_PATCH_VERSION) ")";
+#elif LAME_BETA_VERSION
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) " (beta " STR(LAME_PATCH_VERSION) ")";
+#elif LAME_RELEASE_VERSION && (LAME_PATCH_VERSION > 0)
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) "." STR(LAME_PATCH_VERSION);
+#else
+ static /*@observer@ */ const char *const str =
+ STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION);
+#endif
+
+ return str;
+}
+
+/*! Get the _very_ short LAME version string. */
+/*!
+ It's used in the LAME VBR tag only.
+
+ \param void
+ \return a pointer to the short version of the LAME version string.
+*/
+const char *
+get_lame_very_short_version(void)
+{
+ /* adding date and time to version string makes it harder for output
+ validation */
+#if LAME_ALPHA_VERSION
+#define P "a"
+#elif LAME_BETA_VERSION
+#define P "b"
+#elif LAME_RELEASE_VERSION && (LAME_PATCH_VERSION > 0)
+#define P "r"
+#else
+#define P ""
+#endif
+ static /*@observer@ */ const char *const str =
+#if (LAME_PATCH_VERSION > 0)
+ "LAME" STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) P STR(LAME_PATCH_VERSION)
+#else
+ "LAME" STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) P
+#endif
+ ;
+ return str;
+}
+
+/*! Get the _very_ short LAME version string. */
+/*!
+ It's used in the LAME VBR tag only, limited to 9 characters max.
+ Due to some 3rd party HW/SW decoders, it has to start with LAME.
+
+ \param void
+ \return a pointer to the short version of the LAME version string.
+ */
+const char*
+get_lame_tag_encoder_short_version(void)
+{
+ static /*@observer@ */ const char *const str =
+ /* FIXME: new scheme / new version counting / drop versioning here ? */
+ "LAME" STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) P
+ ;
+ return str;
+}
+
+/*! Get the version string for GPSYCHO. */
+/*!
+ \param void
+ \return a pointer to a string which describes the version of GPSYCHO.
+*/
+const char *
+get_psy_version(void)
+{
+#if PSY_ALPHA_VERSION > 0
+ static /*@observer@ */ const char *const str =
+ STR(PSY_MAJOR_VERSION) "." STR(PSY_MINOR_VERSION)
+ " (alpha " STR(PSY_ALPHA_VERSION) ", " __DATE__ " " __TIME__ ")";
+#elif PSY_BETA_VERSION > 0
+ static /*@observer@ */ const char *const str =
+ STR(PSY_MAJOR_VERSION) "." STR(PSY_MINOR_VERSION)
+ " (beta " STR(PSY_BETA_VERSION) ", " __DATE__ ")";
+#else
+ static /*@observer@ */ const char *const str =
+ STR(PSY_MAJOR_VERSION) "." STR(PSY_MINOR_VERSION);
+#endif
+
+ return str;
+}
+
+
+/*! Get the URL for the LAME website. */
+/*!
+ \param void
+ \return a pointer to a string which is a URL for the LAME website.
+*/
+const char *
+get_lame_url(void)
+{
+ static /*@observer@ */ const char *const str = LAME_URL;
+
+ return str;
+}
+
+
+/*! Get the numerical representation of the version. */
+/*!
+ Writes the numerical representation of the version of LAME and
+ GPSYCHO into lvp.
+
+ \param lvp
+*/
+void
+get_lame_version_numerical(lame_version_t * lvp)
+{
+ static /*@observer@ */ const char *const features = ""; /* obsolete */
+
+ /* generic version */
+ lvp->major = LAME_MAJOR_VERSION;
+ lvp->minor = LAME_MINOR_VERSION;
+#if LAME_ALPHA_VERSION
+ lvp->alpha = LAME_PATCH_VERSION;
+ lvp->beta = 0;
+#elif LAME_BETA_VERSION
+ lvp->alpha = 0;
+ lvp->beta = LAME_PATCH_VERSION;
+#else
+ lvp->alpha = 0;
+ lvp->beta = 0;
+#endif
+
+ /* psy version */
+ lvp->psy_major = PSY_MAJOR_VERSION;
+ lvp->psy_minor = PSY_MINOR_VERSION;
+ lvp->psy_alpha = PSY_ALPHA_VERSION;
+ lvp->psy_beta = PSY_BETA_VERSION;
+
+ /* compile time features */
+ /*@-mustfree@ */
+ lvp->features = features;
+ /*@=mustfree@ */
+}
+
+
+const char *
+get_lame_os_bitness(void)
+{
+ static /*@observer@ */ const char *const strXX = "";
+ static /*@observer@ */ const char *const str32 = "32bits";
+ static /*@observer@ */ const char *const str64 = "64bits";
+
+ switch (sizeof(void *)) {
+ case 4:
+ return str32;
+
+ case 8:
+ return str64;
+
+ default:
+ return strXX;
+ }
+}
+
+/* end of version.c */
diff --git a/libnative/src/main/cpp/module/mp3/lame/version.h b/libnative/src/main/cpp/module/mp3/lame/version.h
new file mode 100644
index 0000000000000000000000000000000000000000..640a0f343b457acd0ceb9941e1cefe5dfc2d39bc
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/lame/version.h
@@ -0,0 +1,68 @@
+/*
+ * Version numbering for LAME.
+ *
+ * Copyright (c) 1999 A.L. Faber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LAME_VERSION_H
+#define LAME_VERSION_H
+
+
+/*
+ * To make a string from a token, use the # operator:
+ */
+#ifndef STR
+# define __STR(x) #x
+# define STR(x) __STR(x)
+#endif
+
+# define LAME_URL "http://lame.sf.net"
+
+
+# define LAME_MAJOR_VERSION 3 /* Major version number */
+# define LAME_MINOR_VERSION 99 /* Minor version number */
+# define LAME_TYPE_VERSION 2 /* 0:alpha 1:beta 2:release */
+# define LAME_PATCH_VERSION 5 /* Patch level */
+# define LAME_ALPHA_VERSION (LAME_TYPE_VERSION==0)
+# define LAME_BETA_VERSION (LAME_TYPE_VERSION==1)
+# define LAME_RELEASE_VERSION (LAME_TYPE_VERSION==2)
+
+# define PSY_MAJOR_VERSION 1 /* Major version number */
+# define PSY_MINOR_VERSION 0 /* Minor version number */
+# define PSY_ALPHA_VERSION 0 /* Set number if this is an alpha version, otherwise zero */
+# define PSY_BETA_VERSION 0 /* Set number if this is a beta version, otherwise zero */
+
+#if LAME_ALPHA_VERSION
+#define LAME_PATCH_LEVEL_STRING " alpha " STR(LAME_PATCH_VERSION)
+#endif
+#if LAME_BETA_VERSION
+#define LAME_PATCH_LEVEL_STRING " beta " STR(LAME_PATCH_VERSION)
+#endif
+#if LAME_RELEASE_VERSION
+#if LAME_PATCH_VERSION
+#define LAME_PATCH_LEVEL_STRING " release " STR(LAME_PATCH_VERSION)
+#else
+#define LAME_PATCH_LEVEL_STRING ""
+#endif
+#endif
+
+# define LAME_VERSION_STRING STR(LAME_MAJOR_VERSION) "." STR(LAME_MINOR_VERSION) LAME_PATCH_LEVEL_STRING
+
+#endif /* LAME_VERSION_H */
+
+/* End of version.h */
diff --git a/libnative/src/main/cpp/module/mp3/mp3.cpp b/libnative/src/main/cpp/module/mp3/mp3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..88a612bf401dff014b1fb8b6f64894aab0ed96ab
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/mp3.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Lame for mp3
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+#include "mp3.h"
+#include "lame/lame.h"
+#include "../../utils/logger.h"
+
+static lame_global_flags *gfp = nullptr;
+
+void lameInitInternal(int inSampleRate, int outChannel, int outSampleRate, int outBitRate, int quality) {
+ if(gfp != nullptr){
+ lameCloseInternal();
+ }
+ gfp = lame_init();
+ LOGI("init lame library success!");
+ lame_set_in_samplerate(gfp,inSampleRate);
+ lame_set_num_channels(gfp,outChannel);
+ lame_set_out_samplerate(gfp,outSampleRate);
+ lame_set_brate(gfp,outBitRate);
+ lame_set_quality(gfp,quality);
+ lame_init_params(gfp);
+ LOGI("config lame library success!");
+}
+
+int lameEncodeInternal(short* leftBuf, short* rightBuf, int sampleRate, unsigned char* mp3Buf, int len) {
+ int ret = lame_encode_buffer(gfp,leftBuf,rightBuf,sampleRate,mp3Buf,len);
+ if (ret < 0) {
+ LOG_E("encode pcm data failed, err = %d",ret);
+ }
+ return ret;
+}
+
+int lameFlushInternal(unsigned char* mp3Buf, int len) {
+ int ret = lame_encode_flush(gfp,mp3Buf,len);
+ if (ret <= 0) {
+ LOG_E("flush lame failed, err = %d", ret);
+ }
+ return ret;
+}
+
+void lameCloseInternal() {
+ lame_close(gfp);
+ gfp = nullptr;
+ LOGI("close lame success!");
+}
\ No newline at end of file
diff --git a/libnative/src/main/cpp/module/mp3/mp3.h b/libnative/src/main/cpp/module/mp3/mp3.h
new file mode 100644
index 0000000000000000000000000000000000000000..73c7bd98b18352a01253a5fca76820999dac2594
--- /dev/null
+++ b/libnative/src/main/cpp/module/mp3/mp3.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Lame for mp3
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+
+#ifndef ANDROIDUSBCAMERA_MP3_H
+#define ANDROIDUSBCAMERA_MP3_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void lameInitInternal(int inSampleRate, int outChannel, int outSampleRate, int outBitRate, int quality);
+int lameEncodeInternal(short* leftBuf, short* rightBuf, int sampleRate, unsigned char* mp3Buf, int len);
+int lameFlushInternal(unsigned char* mp3Buf, int len);
+void lameCloseInternal();
+#ifdef __cplusplus
+};
+#endif
+#endif //ANDROIDUSBCAMERA_MP3_H
diff --git a/libnative/src/main/cpp/module/yuv/yuv.cpp b/libnative/src/main/cpp/module/yuv/yuv.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00bd62ab7a1a9c3b7667ba19d03ed8eab1aa9c38
--- /dev/null
+++ b/libnative/src/main/cpp/module/yuv/yuv.cpp
@@ -0,0 +1,53 @@
+/**
+ * yuv handle
+ *
+ * NV21:YYYYYYYY VUVU
+ * YV12:YYYYYYYY VV UU
+ * YUV420sp:YYYYYYYY UVUV
+ * YUV420p:YYYYYYYY UU VV
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+
+#include "yuv.h"
+
+void *yuv420spToNv21Internal(char* srcData, char* destData, int width, int height) {
+ int yLength = width * height;
+ int uLength = yLength / 4;
+ memcpy(destData,srcData,yLength);
+ for(int i=0; i
+
+void *yuv420spToNv21Internal(char* srcData, char* destData, int width, int height);
+void *nv21ToYuv420spInternal(char* srcData, char* destData, int width, int height);
+void *nv21ToYuv420spWithMirrorInternal(char* srcData, char* destData, int width, int height);
+void *nv21ToYuv420pInternal(char* srcData, char* destData, int width, int height);
+void *nv21ToYuv420pWithMirrorInternal(char* srcData, char* destData, int width, int height);
+
+#ifdef __cplusplus
+};
+#endif
+#endif //ANDROIDUSBCAMERA_YUV_H
diff --git a/libnative/src/main/cpp/nativelib.cpp b/libnative/src/main/cpp/nativelib.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3c10afb34dc848e841351da5bf3017ab4c97b2d
--- /dev/null
+++ b/libnative/src/main/cpp/nativelib.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Signature mapping table:
+ * JNIType Java/Kotlin JniSign
+ * jbyte byte/Byte B
+ * jshort short/Short S
+ * jint int/Int I
+ * jlong long/Long J
+ * jfloat float/Float F
+ * jdouble double/Double D
+ * jboolean boolean/Boolean Z
+ * jchar char/Char C
+ * void void/- V
+ * jclass Class/Class Ljava/lang/Class
+ * jobject Object/- Ljava/lang/Object
+ * jstring String/String Ljava/lang/String
+ * jobjectArray Object[]/- [Ljava/lang/Object
+ * jobjectArray String[]/- [Ljava/lang/String
+ * jbyteArray byte[]/ByteArray [B
+ * jshortArray short[]/ShortArray [S
+ * jintArray int[]/IntArray [I
+ * jlongArray long[]/LongArray [J
+ * jfloatArray float[]/FloatArray [F
+ * jdoubleArray double[]/DoubleArray [D
+ * jbooleanArray boolean[]/BooleanArray [Z
+ * jcharArray char[]/CharArray [C
+ */
+#include
+#include
+#include "utils/logger.h"
+#include "proxy/proxy_yuv.h"
+#include "proxy/proxy_mp3.h"
+
+#define NUM_METHODS(x) ((int)(sizeof(x)/ sizeof(x[0])))
+JavaVM *globalJvm;
+const char * yuvClsPath = "com/jiangdg/natives/YUVUtils";
+const char * lameClsPath = "com/jiangdg/natives/LameMp3";
+
+static JNINativeMethod g_yuv_methods[] = {
+ {"yuv420spToNv21", "([BII)V", (void *)yuv420spToNv21},
+ {"nv21ToYuv420sp", "([BII)V", (void *)nv21ToYuv420sp},
+ {"nv21ToYuv420spWithMirror", "([BII)V", (void *)nv21ToYuv420spWithMirror},
+ {"nv21ToYuv420p", "([BII)V", (void *)nv21ToYuv420p},
+ {"nv21ToYuv420pWithMirror", "([BII)V", (void *)nv21ToYuv420pWithMirror},
+};
+
+static JNINativeMethod g_lame_methods[] = {
+ {"lameInit", "(IIIII)V", (void *)lameInit},
+ {"lameEncode", "([S[SI[B)I", (void *)lameEncode},
+ {"lameFlush", "([B)I", (void *)lameFlush},
+ {"lameClose", "()V", (void *)lameClose},
+};
+
+extern "C"
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) {
+ globalJvm = jvm;
+
+ // 获取jvm中的JNIEnv实例对象
+ JNIEnv *env;
+ if(JNI_OK != jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4)) {
+ LOGE("Get JNIEnv failed");
+ return JNI_ERR;
+ }
+ // 注册所有native类的方法
+ jclass yuvLcs = env->FindClass(yuvClsPath);
+ int ret = env->RegisterNatives(yuvLcs, g_yuv_methods, NUM_METHODS(g_yuv_methods));
+ if( ret < 0) {
+ LOG_E("Register yuv transform natives failed, ret = %d", ret);
+ }
+ jclass lameLcs = env->FindClass(lameClsPath);
+ ret = env->RegisterNatives(lameLcs, g_lame_methods, NUM_METHODS(g_lame_methods));
+ if( ret < 0) {
+ LOG_E("Register lame mp3 natives failed, ret = %d", ret);
+ }
+ LOGI("JNI_OnLoad success!");
+ return JNI_VERSION_1_4;
+}
+
+extern "C"
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* jvm, void* reserved) {
+ if(jvm) {
+ jvm->DestroyJavaVM();
+ }
+ globalJvm = nullptr;
+ LOGI("JNI_OnUnload success!");
+}
\ No newline at end of file
diff --git a/libnative/src/main/cpp/proxy/proxy_mp3.cpp b/libnative/src/main/cpp/proxy/proxy_mp3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..007548d70bb6e7e8d3d89a51c859eb720bc50570
--- /dev/null
+++ b/libnative/src/main/cpp/proxy/proxy_mp3.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Proxy of mp3.
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+#include "proxy_mp3.h"
+#include "../module/mp3/mp3.h"
+#include "../utils/logger.h"
+
+void lameInit(JNIEnv *env, jobject instance, jint inSampleRate, jint outChannel, jint outSampleRate,
+ jint outBitRate, jint quality) {
+ lameInitInternal(inSampleRate, outChannel, outSampleRate, outBitRate, quality);
+}
+
+int lameEncode(JNIEnv *env, jobject instance, jshortArray leftBuf_, jshortArray rightBuf_,
+ jint sampleRate, jbyteArray mp3Buf_) {
+ jint ret = -1;
+ if(leftBuf_ == nullptr || mp3Buf_ == nullptr){
+ LOGI("data can't be null");
+ return ret;
+ }
+ jshort *leftBuf;
+ jshort *rightBuf = nullptr;
+ leftBuf = env->GetShortArrayElements(leftBuf_, nullptr);
+ if(rightBuf_ != nullptr){
+ rightBuf = env->GetShortArrayElements(rightBuf_, nullptr);
+ }
+ jbyte *mp3Buf = env->GetByteArrayElements(mp3Buf_, nullptr);
+ jsize readSizes = env->GetArrayLength(mp3Buf_);
+ ret = lameEncodeInternal(leftBuf, rightBuf, sampleRate, reinterpret_cast(mp3Buf), readSizes);
+ env->ReleaseShortArrayElements(leftBuf_, leftBuf, 0);
+ if(rightBuf_ != nullptr){
+ env->ReleaseShortArrayElements(rightBuf_, rightBuf, 0);
+ }
+ env->ReleaseByteArrayElements(mp3Buf_, mp3Buf, 0);
+ return ret;
+}
+
+jint lameFlush(JNIEnv *env, jobject instance, jbyteArray mp3buf_) {
+ jbyte *mp3buf = env->GetByteArrayElements(mp3buf_, nullptr);
+ jsize len = env->GetArrayLength(mp3buf_);
+ jint ret = lameFlushInternal(reinterpret_cast(mp3buf), len);
+ env->ReleaseByteArrayElements(mp3buf_, mp3buf, 0);
+ return ret;
+}
+
+void lameClose(JNIEnv *env, jobject instance) {
+ lameCloseInternal();
+}
diff --git a/libnative/src/main/cpp/proxy/proxy_mp3.h b/libnative/src/main/cpp/proxy/proxy_mp3.h
new file mode 100644
index 0000000000000000000000000000000000000000..e283e2fedf3fccc4d1dbd3b720be6db1057c2c62
--- /dev/null
+++ b/libnative/src/main/cpp/proxy/proxy_mp3.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Proxy of mp3.
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+#ifndef ANDROIDUSBCAMERA_PROXY_MP3_H
+#define ANDROIDUSBCAMERA_PROXY_MP3_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+void lameInit(JNIEnv *env, jobject instance, jint inSampleRate, jint outChannel, jint outSampleRate, jint outBitRate, jint quality);
+jint lameEncode(JNIEnv *env, jobject instance, jshortArray leftBuf_, jshortArray rightBuf, jint sampleRate, jbyteArray mp3Buf);
+jint lameFlush(JNIEnv *env, jobject instance, jbyteArray mp3Buf);
+void lameClose(JNIEnv *env, jobject instance);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif //ANDROIDUSBCAMERA_PROXY_MP3_H
diff --git a/libnative/src/main/cpp/proxy/proxy_yuv.cpp b/libnative/src/main/cpp/proxy/proxy_yuv.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81e6dc70bc13ff894fdb1e70f77b3c8dcdc7d41f
--- /dev/null
+++ b/libnative/src/main/cpp/proxy/proxy_yuv.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Proxy of yuv.
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+#include "proxy_yuv.h"
+
+void yuv420spToNv21(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height) {
+ if(! data || width == 0 || height == 0) {
+ LOGE("Parameters error in nv21ToYuv420sp");
+ return;
+ }
+ jbyte *srcData = env->GetByteArrayElements(data, JNI_FALSE);
+ jsize srcLen = env->GetArrayLength(data);
+ char *dest = (char *)malloc(srcLen);
+ yuv420spToNv21Internal((char *)srcData,dest, width, height);
+ env->SetByteArrayRegion(data,0,srcLen,(jbyte *)dest);
+ env->ReleaseByteArrayElements(data, srcData, 0);
+ free(dest);
+}
+
+void nv21ToYuv420sp(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height) {
+ if(! data || width == 0 || height == 0) {
+ LOGE("Parameters error in nv21ToYuv420sp");
+ return;
+ }
+ jbyte *srcData = env->GetByteArrayElements(data, JNI_FALSE);
+ jsize srcLen = env->GetArrayLength(data);
+ char *dest = (char *)malloc(srcLen);
+ nv21ToYuv420spInternal((char *)srcData,dest, width, height);
+ env->SetByteArrayRegion(data,0,srcLen,(jbyte *)dest);
+ env->ReleaseByteArrayElements(data, srcData, 0);
+ free(dest);
+}
+
+void nv21ToYuv420spWithMirror(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height) {
+ if(! data || width == 0 || height == 0) {
+ LOGE("Parameters error in nv21ToYuv420spWithMirror");
+ return;
+ }
+ jbyte *srcData = env->GetByteArrayElements(data, JNI_FALSE);
+ jsize srcLen = env->GetArrayLength(data);
+ char *dest = (char *)malloc(srcLen);
+ nv21ToYuv420spWithMirrorInternal((char *)srcData,dest, width, height);
+ env->SetByteArrayRegion(data,0,srcLen,(jbyte *)dest);
+ env->ReleaseByteArrayElements(data, srcData, 0);
+ free(dest);
+}
+
+void nv21ToYuv420p(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height) {
+ if(! data || width == 0 || height == 0) {
+ LOGE("Parameters error in nv21ToYuv420p");
+ return;
+ }
+ jbyte *srcData = env->GetByteArrayElements(data, JNI_FALSE);
+ jsize srcLen = env->GetArrayLength(data);
+ char *dest = (char *)malloc(srcLen);
+ nv21ToYuv420pInternal((char *)srcData,dest, width, height);
+ env->SetByteArrayRegion(data,0,srcLen,(jbyte *)dest);
+ env->ReleaseByteArrayElements(data, srcData, 0);
+ free(dest);
+}
+
+void nv21ToYuv420pWithMirror(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height) {
+ if(! data || width == 0 || height == 0) {
+ LOGE("Parameters error in nv21ToYuv420pWithMirror");
+ return;
+ }
+ jbyte *srcData = env->GetByteArrayElements(data, JNI_FALSE);
+ jsize srcLen = env->GetArrayLength(data);
+ char *dest = (char *)malloc(srcLen);
+ nv21ToYuv420pWithMirrorInternal((char *)srcData,dest, width, height);
+ env->SetByteArrayRegion(data,0,srcLen,(jbyte *)dest);
+ env->ReleaseByteArrayElements(data, srcData, 0);
+ free(dest);
+}
diff --git a/libnative/src/main/cpp/proxy/proxy_yuv.h b/libnative/src/main/cpp/proxy/proxy_yuv.h
new file mode 100644
index 0000000000000000000000000000000000000000..87b70a3070a0319ab7de8cea1eaf8e4caa738bf2
--- /dev/null
+++ b/libnative/src/main/cpp/proxy/proxy_yuv.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Proxy of yuv.
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+#ifndef ANDROIDUSBCAMERA_PROXY_YUV_H
+#define ANDROIDUSBCAMERA_PROXY_YUV_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include "../module/yuv/yuv.h"
+#include "../utils/logger.h"
+
+void yuv420spToNv21(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height);
+void nv21ToYuv420sp(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height);
+void nv21ToYuv420spWithMirror(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height);
+void nv21ToYuv420p(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height);
+void nv21ToYuv420pWithMirror(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height);
+
+#ifdef __cplusplus
+};
+#endif
+#endif //ANDROIDUSBCAMERA_PROXY_YUV_H
diff --git a/libnative/src/main/cpp/utils/logger.cpp b/libnative/src/main/cpp/utils/logger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9650fa00bb141eb291d92012080d57aca62e99b0
--- /dev/null
+++ b/libnative/src/main/cpp/utils/logger.cpp
@@ -0,0 +1,10 @@
+/**
+ *
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+//
+// Created by Jiangdongguo on 2022/2/18.
+//
+
+#include "logger.h"
diff --git a/libnative/src/main/cpp/utils/logger.h b/libnative/src/main/cpp/utils/logger.h
new file mode 100644
index 0000000000000000000000000000000000000000..24b12086f47e14e01b402b94890f883ad133ce21
--- /dev/null
+++ b/libnative/src/main/cpp/utils/logger.h
@@ -0,0 +1,25 @@
+/**
+ *
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+
+#ifndef ANDROIDUSBCAMERA_LOGGER_H
+#define ANDROIDUSBCAMERA_LOGGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define TAG "AUSBC"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, "%s", __VA_ARGS__)
+#define LOG_I(format, ...) __android_log_print(ANDROID_LOG_INFO, TAG, format, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, "%s", __VA_ARGS__)
+#define LOG_E(format, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, format, __VA_ARGS__)
+
+#ifdef __cplusplus
+};
+#endif
+#endif //ANDROIDUSBCAMERA_LOGGER_H
diff --git a/libnative/src/main/java/com/jiangdg/natives/LameMp3.kt b/libnative/src/main/java/com/jiangdg/natives/LameMp3.kt
new file mode 100644
index 0000000000000000000000000000000000000000..89820ab39936a9adbc026436dd0a814fdf311b9a
--- /dev/null
+++ b/libnative/src/main/java/com/jiangdg/natives/LameMp3.kt
@@ -0,0 +1,55 @@
+package com.jiangdg.natives
+
+/** pcm to mp3
+ *
+ * @author Created by jiangdg on 2022/3/2
+ */
+object LameMp3 {
+
+ init {
+ System.loadLibrary("nativelib")
+ }
+
+ /** Initialize the lame library and configure related information
+ *
+ * @param inSampleRate pcm format audio sample rate
+ * @param outChannel number of audio channels in pcm format
+ * @param outSampleRate mp3 format audio sample rate
+ * @param outBitRate mp3 format audio bit rate
+ * @param quality mp3 format audio quality, 0~9, slowest and worst~fastest and best
+ */
+ external fun lameInit(
+ inSampleRate: Int,
+ outChannel: Int,
+ outSampleRate: Int,
+ outBitRate: Int,
+ quality: Int
+ )
+
+ /** Encode pcm into mp3 format
+ *
+ * @param leftBuf left pcm data
+ * @param rightBuf right pcm data, if it is mono, it is the same
+ * @param sampleRate read in pcm byte size
+ * @param mp3Buf store mp3 data buffer
+ * @return encoded data byte length
+ */
+ external fun lameEncode(
+ leftBuf: ShortArray?,
+ rightBuf: ShortArray?,
+ sampleRate: Int,
+ mp3Buf: ByteArray?
+ ):Int
+
+ /** save mp3 audio stream to file
+ *
+ * @param mp3buf mp3 data stream
+ * @return data stream length rty
+ */
+ external fun lameFlush(mp3buf: ByteArray?): Int
+
+ /**
+ * Release lame library resources
+ */
+ external fun lameClose()
+}
\ No newline at end of file
diff --git a/libnative/src/main/java/com/jiangdg/natives/YUVUtils.kt b/libnative/src/main/java/com/jiangdg/natives/YUVUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7770643d5324bffc4c8055a515f8a6f3b46032bc
--- /dev/null
+++ b/libnative/src/main/java/com/jiangdg/natives/YUVUtils.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.natives
+
+/** YUV format transform
+ *
+ * @author Created by jiangdg on 2022/2/18
+ */
+object YUVUtils {
+ init {
+ System.loadLibrary("nativelib")
+ }
+
+ external fun yuv420spToNv21(data: ByteArray, width: Int, height: Int)
+ external fun nv21ToYuv420sp(data: ByteArray, width: Int, height: Int)
+ external fun nv21ToYuv420spWithMirror(data: ByteArray, width: Int, height: Int)
+ external fun nv21ToYuv420p(data: ByteArray, width: Int, height: Int)
+ external fun nv21ToYuv420pWithMirror(data: ByteArray, width: Int, height: Int)
+}
\ No newline at end of file
diff --git a/libnative/src/test/java/com/jiangdg/natives/ExampleUnitTest.kt b/libnative/src/test/java/com/jiangdg/natives/ExampleUnitTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..64b8a20baf4b6220c7ec032aa6ed5b66fd6a7506
--- /dev/null
+++ b/libnative/src/test/java/com/jiangdg/natives/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.jiangdg.natives
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/libpush/.gitignore b/libpush/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8
--- /dev/null
+++ b/libpush/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/libpush/build.gradle b/libpush/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..640f3d7653d1cb7cb3345e7c5d6b7424ecac3df9
--- /dev/null
+++ b/libpush/build.gradle
@@ -0,0 +1,42 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'com.google.android.material:material:1.3.0'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/libpush/consumer-rules.pro b/libpush/consumer-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libpush/proguard-rules.pro b/libpush/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/libpush/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/libpush/src/androidTest/java/com/jiangdg/push/ExampleInstrumentedTest.kt b/libpush/src/androidTest/java/com/jiangdg/push/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..83f8a02416c326e6f7e44a4e1bdf99116c81a90a
--- /dev/null
+++ b/libpush/src/androidTest/java/com/jiangdg/push/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.jiangdg.push
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.jiangdg.push.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/libpush/src/main/AndroidManifest.xml b/libpush/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..84807c1e6a40587f4df4f67d53e95108d7268a57
--- /dev/null
+++ b/libpush/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/libpush/src/test/java/com/jiangdg/push/ExampleUnitTest.kt b/libpush/src/test/java/com/jiangdg/push/ExampleUnitTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dc162afc203049fce6a0e4d5fab7bb2f08d6dcea
--- /dev/null
+++ b/libpush/src/test/java/com/jiangdg/push/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.jiangdg.push
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/libutils/.gitignore b/libutils/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8
--- /dev/null
+++ b/libutils/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/libutils/build.gradle b/libutils/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..f621a4fda3fd0298103231120e22123fd7db8152
--- /dev/null
+++ b/libutils/build.gradle
@@ -0,0 +1,49 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ compileSdkVersion rootProject.ext.versionCompiler
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.versionTarget
+ versionCode rootProject.ext.versionCode
+ versionName rootProject.ext.versionNameString
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+ // utils
+ api 'com.gyf.immersionbar:immersionbar:3.0.0'
+ implementation "com.github.bumptech.glide:glide:4.10.0"
+ implementation "com.github.bumptech.glide:okhttp3-integration:4.10.0"
+ implementation "com.zlc.glide:webpdecoder:1.6.4.9.0"
+ implementation 'com.tencent:mmkv:1.2.12'
+}
\ No newline at end of file
diff --git a/libutils/consumer-rules.pro b/libutils/consumer-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libutils/proguard-rules.pro b/libutils/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/libutils/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/libutils/src/androidTest/java/com/jiangdg/utils/ExampleInstrumentedTest.kt b/libutils/src/androidTest/java/com/jiangdg/utils/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5c6c199629b9c6d6ef2785c1bf8f2b120ae9aeb5
--- /dev/null
+++ b/libutils/src/androidTest/java/com/jiangdg/utils/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.jiangdg.utils
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.jiangdg.utils.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/libutils/src/main/AndroidManifest.xml b/libutils/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a881c96c5fa2fc33d7b02fdf139f0537ec3db864
--- /dev/null
+++ b/libutils/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/libutils/src/main/java/com/jiangdg/utils/MMKVUtils.kt b/libutils/src/main/java/com/jiangdg/utils/MMKVUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2d4e7eeb79a9f8a504933ba27eeb3577eb102aa0
--- /dev/null
+++ b/libutils/src/main/java/com/jiangdg/utils/MMKVUtils.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.utils
+
+import android.content.Context
+import android.os.Parcelable
+import com.tencent.mmkv.MMKV
+
+/** Global haredPreference wrapper by MMKV
+ *
+ * @author Created by jiangdg on 2022/3/30
+ */
+object MMKVUtils {
+
+ private val mKv: MMKV by lazy {
+ MMKV.defaultMMKV()
+ }
+
+ /**
+ * Init MMKV
+ *
+ * @param context
+ */
+ fun init(context: Context) {
+ MMKV.initialize(context.applicationContext)
+ }
+
+ /**
+ * save value to SharedPreference
+ *
+ * @param key key
+ * @param value value, such as Int,String,Boolean...etc.
+ */
+ fun set(key: String, value: Any) {
+ when(value) {
+ is String -> mKv.encode(key, value)
+ is Int -> mKv.encode(key, value)
+ is Double -> mKv.encode(key, value)
+ is Float -> mKv.encode(key, value)
+ is Boolean -> mKv.encode(key, value)
+ is Long -> mKv.encode(key, value)
+ is ByteArray -> mKv.encode(key, value)
+ is Parcelable -> mKv.encode(key, value)
+ else -> throw IllegalStateException("Unsupported value type")
+ }
+ }
+
+ /**
+ * Get sharedPreference string value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference string value
+ */
+ fun getString(key: String, defaultValue: String?=null): String? {
+ return mKv.decodeString(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Int value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference Int value
+ */
+ fun getInt(key: String, defaultValue: Int=0): Int {
+ return mKv.decodeInt(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Long value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference Long value
+ */
+ fun getLong(key: String, defaultValue: Long=0L): Long {
+ return mKv.decodeLong(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Double value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference Double value
+ */
+ fun getDouble(key: String, defaultValue: Double=0.0): Double {
+ return mKv.decodeDouble(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Float value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference Float value
+ */
+ fun getFloat(key: String, defaultValue: Float=0F): Float {
+ return mKv.decodeFloat(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Boolean value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference Boolean value
+ */
+ fun getBoolean(key: String, defaultValue: Boolean=false): Boolean {
+ return mKv.decodeBool(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference ByteArray value
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return sharedPreference ByteArray value
+ */
+ fun getByteArray(key: String, defaultValue: ByteArray?=null): ByteArray? {
+ return mKv.decodeBytes(key, defaultValue)
+ }
+
+ /**
+ * Get sharedPreference Parcelable value
+ *
+ * @param key key
+ * @param clz Parcelable class
+ * @return sharedPreference Parcelable value
+ */
+ fun getParcelable(key: String, clz: Class): T? {
+ return mKv.decodeParcelable(key, clz)
+ }
+}
diff --git a/libutils/src/main/java/com/jiangdg/utils/imageloader/GlideLoader.kt b/libutils/src/main/java/com/jiangdg/utils/imageloader/GlideLoader.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1fef05ff6cab77eb674ce792220c2f7022a44ef5
--- /dev/null
+++ b/libutils/src/main/java/com/jiangdg/utils/imageloader/GlideLoader.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.utils.imageloader
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.widget.ImageView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import com.bumptech.glide.Glide
+import com.bumptech.glide.RequestManager
+import com.bumptech.glide.integration.webp.decoder.WebpDrawable
+import com.bumptech.glide.integration.webp.decoder.WebpDrawableTransformation
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.Transformation
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.Target
+import com.jiangdg.utils.R
+import java.lang.IllegalArgumentException
+
+/**GlideImageLoader by glide
+ *
+ * @param target imageview owner
+ *
+ * @author Created by jiangdg on 2022/3/16
+ */
+class GlideLoader(target: T) : ILoader {
+ private var mRequestManager: RequestManager? = null
+
+ init {
+ mRequestManager = when (target) {
+ is Fragment -> Glide.with(target)
+ is FragmentActivity -> Glide.with(target)
+ is Activity -> Glide.with(target)
+ is Context -> Glide.with(target)
+ is View -> Glide.with(target)
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun load(imageView: ImageView, url: String?, placeHolder: Int) {
+ val centerCrop: Transformation = CenterCrop()
+ mRequestManager!!.load(url).optionalTransform(centerCrop)
+ .optionalTransform(WebpDrawable::class.java, WebpDrawableTransformation(centerCrop))
+ .placeholder(placeHolder)
+ .into(imageView)
+ }
+
+ override fun load(imageView: ImageView, url: String?) {
+ val centerCrop: Transformation = CenterCrop()
+ mRequestManager!!.load(url).optionalTransform(centerCrop)
+ .optionalTransform(WebpDrawable::class.java, WebpDrawableTransformation(centerCrop))
+ .placeholder(R.drawable.imageloader_default_cover_bg)
+ .into(imageView)
+ }
+
+ override fun load(imageView: ImageView, resId: Int) {
+ val centerCrop: Transformation = CenterCrop()
+ mRequestManager!!.load(resId).optionalTransform(centerCrop)
+ .optionalTransform(WebpDrawable::class.java, WebpDrawableTransformation(centerCrop))
+ .placeholder(R.drawable.imageloader_default_cover_bg)
+ .into(imageView)
+ }
+
+ override fun load(
+ imageView: ImageView,
+ url: String?,
+ placeHolder: Int,
+ bitmapTransformation: BitmapTransformation?
+ ) {
+ mRequestManager!!.load(url).optionalTransform(bitmapTransformation!!)
+ .optionalTransform(
+ WebpDrawable::class.java,
+ WebpDrawableTransformation(bitmapTransformation)
+ )
+ .placeholder(placeHolder).into(imageView)
+ }
+
+ @SuppressLint("CheckResult")
+ override fun loadRounded(imageView: ImageView, url: String?, placeHolder: Int, radius: Float) {
+ RequestOptions().apply {
+ if (radius >= 0) {
+ transform(CenterCrop(), RoundedCorners(dp2px(imageView.context, radius)))
+ } else {
+ transform(RoundedCorners(dp2px(imageView.context, radius)))
+ }
+ }.also { options ->
+ mRequestManager!!.load(url)
+ .placeholder(placeHolder)
+ .apply(options)
+ .into(imageView)
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ override fun loadRounded(
+ imageView: ImageView,
+ url: String?,
+ placeHolder: Drawable?,
+ radius: Float
+ ) {
+ RequestOptions().apply {
+ if (radius >= 0) {
+ transform(CenterCrop(), RoundedCorners(dp2px(imageView.context, radius)))
+ } else {
+ transform(RoundedCorners(dp2px(imageView.context, radius)))
+ }
+ }.also { options ->
+ mRequestManager!!.load(url)
+ .placeholder(placeHolder)
+ .apply(options)
+ .into(imageView)
+ }
+ }
+
+ override fun loadRounded(imageView: ImageView, url: String?, radius: Float) {
+ loadRounded(imageView, url, R.drawable.imageloader_default_cover_bg, radius)
+ }
+
+ override fun loadCircle(imageView: ImageView, url: String?, placeHolder: Int) {
+ mRequestManager?.apply {
+ this.load(url)
+ .placeholder(placeHolder)
+ .apply(RequestOptions.bitmapTransform(CircleCrop()))
+ .into(imageView)
+ }
+ }
+
+ override fun loadCircle(imageView: ImageView, url: String?) {
+ mRequestManager?.apply {
+ this.load(url)
+ .placeholder(R.drawable.imageloader_default_cover_bg)
+ .apply(RequestOptions.bitmapTransform(CircleCrop()))
+ .into(imageView)
+ }
+ }
+
+ override fun loadCircle(imageView: ImageView, resId: Int, placeHolder: Int) {
+ mRequestManager?.apply {
+ this.load(resId)
+ .placeholder(placeHolder)
+ .apply(RequestOptions.bitmapTransform(CircleCrop()))
+ .into(imageView)
+ }
+ }
+
+ override fun loadCircle(imageView: ImageView, resId: Int) {
+ mRequestManager?.apply {
+ this.load(resId)
+ .placeholder(R.drawable.imageloader_default_cover_bg)
+ .apply(RequestOptions.bitmapTransform(CircleCrop()))
+ .into(imageView)
+ }
+ }
+
+ override fun loadAsBitmap(
+ url: String?,
+ width: Int,
+ height: Int,
+ listener: ILoader.OnLoadedResultListener
+ ) {
+ mRequestManager?.apply {
+ this.asBitmap()
+ .centerCrop()
+ .load(url)
+ .listener(object : RequestListener {
+ override fun onLoadFailed(
+ e: GlideException?,
+ model: Any?,
+ target: Target?,
+ isFirstResource: Boolean
+ ): Boolean {
+ listener.onLoadedFailed(e)
+ return true
+ }
+
+ override fun onResourceReady(
+ resource: Bitmap?,
+ model: Any?,
+ target: Target?,
+ dataSource: DataSource?,
+ isFirstResource: Boolean
+ ): Boolean {
+ listener.onLoadedSuccess(resource)
+ return true
+ }
+
+ })
+ .submit(width, height)
+ }
+ }
+
+ private fun dp2px(context: Context, dpValue: Float): Int {
+ val scale: Float = context.resources.displayMetrics.density
+ return (dpValue * scale + 0.5f).toInt()
+ }
+}
\ No newline at end of file
diff --git a/libutils/src/main/java/com/jiangdg/utils/imageloader/ILoader.kt b/libutils/src/main/java/com/jiangdg/utils/imageloader/ILoader.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bf7745caadc804c845c48e0800ef3db313d1d0e7
--- /dev/null
+++ b/libutils/src/main/java/com/jiangdg/utils/imageloader/ILoader.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.utils.imageloader
+
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
+
+/**
+ * Image loader
+ *
+ * @param T image view
+ * @author Created by jiangdg on 2022/3/16
+ */
+interface ILoader {
+ /**
+ * Load image from url
+ *
+ * @param imageView image view
+ * @param url image uri
+ * @param placeHolder place holder
+ */
+ fun load(imageView: T, url: String?, placeHolder: Int)
+
+ /**
+ * Load image from url width default place holder
+ *
+ * @param imageView image view
+ * @param url image url
+ */
+ fun load(imageView: T, url: String?)
+
+ /**
+ * Load image from resource id
+ *
+ * @param imageView image view
+ * @param resId resource id
+ */
+ fun load(imageView: T, resId: Int)
+
+ /**
+ * Load image from url with transform
+ *
+ * @param imageView image view
+ * @param url image url
+ * @param placeHolder place holder
+ * @param bitmapTransformation transformation
+ */
+ fun load(
+ imageView: T,
+ url: String?,
+ placeHolder: Int,
+ bitmapTransformation: BitmapTransformation?
+ )
+
+ /**
+ * Load rounded from url
+ *
+ * @param imageView image view
+ * @param url image url
+ * @param placeHolder resource id of place holder
+ * @param radius radius of rounded image
+ */
+ fun loadRounded(imageView: T, url: String?, placeHolder: Int, radius: Float)
+
+ /**
+ * Load rounded from url
+ *
+ * @param imageView image view
+ * @param url image url
+ * @param placeHolder drawable type of place holder
+ * @param radius radius of rounded image
+ */
+ fun loadRounded(imageView: T, url: String?, placeHolder: Drawable?, radius: Float)
+
+ /**
+ * Load rounded from url
+ *
+ * @param imageView image view
+ * @param url image url
+ * @param radius radius of rounded image
+ */
+ fun loadRounded(imageView: T, url: String?, radius: Float)
+
+ /**
+ * Load circle from url
+ *
+ * @param imageView image view
+ * @param url image url
+ * @param placeHolder resource id of place holder
+ */
+ fun loadCircle(imageView: T, url: String?, placeHolder: Int)
+
+ /**
+ * Load circle from url
+ *
+ * @param imageView image view
+ * @param url image url
+ */
+ fun loadCircle(imageView: T, url: String?)
+
+ /**
+ * Load circle from url
+ *
+ * @param imageView image view
+ * @param resId image resId
+ * @param placeHolder resource id of place holder
+ */
+ fun loadCircle(imageView: T, resId: Int, placeHolder: Int)
+
+ /**
+ * Load circle from resource id
+ *
+ * @param imageView image view
+ * @param resId image resId
+ */
+ fun loadCircle(imageView: T, resId: Int)
+
+ fun loadAsBitmap(url: String?, width: Int, height: Int, listener: OnLoadedResultListener)
+
+ interface OnLoadedResultListener {
+ fun onLoadedSuccess(bitmap: Bitmap?)
+ fun onLoadedFailed(error: Exception?)
+ }
+}
\ No newline at end of file
diff --git a/libutils/src/main/java/com/jiangdg/utils/imageloader/ImageLoaders.kt b/libutils/src/main/java/com/jiangdg/utils/imageloader/ImageLoaders.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1e503a786a86c35b0b02c0750fdfe8186824b711
--- /dev/null
+++ b/libutils/src/main/java/com/jiangdg/utils/imageloader/ImageLoaders.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-2022 Jiangdg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jiangdg.utils.imageloader
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import android.widget.ImageView
+import androidx.fragment.app.Fragment
+
+/**
+ * Image loaders
+ *
+ * @author Created by jiangdg on 2022/3/16
+ */
+object ImageLoaders {
+ /**
+ * create a glide image loader
+ *
+ * @param fragment target is fragment
+ * @return [GlideLoader]
+ */
+ fun of(fragment: Fragment): ILoader = GlideLoader(fragment)
+
+ /**
+ * create a glide image loader
+ *
+ * @param activity target is activity
+ * @return [GlideLoader]
+ */
+ fun of(activity: Activity): ILoader = GlideLoader(activity)
+
+ /**
+ * create a glide image loader
+ *
+ * @param context target is context
+ * @return [GlideLoader]
+ */
+ fun of(context: Context): ILoader = GlideLoader(context)
+
+ /**
+ * create a glide image loader
+ *
+ * @param view target is view
+ * @return [GlideLoader]
+ */
+ fun of(view: View): ILoader = GlideLoader(view)
+}
\ No newline at end of file
diff --git a/libutils/src/main/res/drawable/effect_none.xml b/libutils/src/main/res/drawable/effect_none.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f2d9c646b94f480217c5652d9e3579e32688ebef
--- /dev/null
+++ b/libutils/src/main/res/drawable/effect_none.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/libutils/src/main/res/drawable/ic_none.png b/libutils/src/main/res/drawable/ic_none.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab54440f9cb80f3f4955b5da0208cfaadf48170a
Binary files /dev/null and b/libutils/src/main/res/drawable/ic_none.png differ
diff --git a/libutils/src/main/res/drawable/imageloader_default_cover_bg.xml b/libutils/src/main/res/drawable/imageloader_default_cover_bg.xml
new file mode 100644
index 0000000000000000000000000000000000000000..db7b2cc5d66b650da66f5ca7cb4801dd1d12f71c
--- /dev/null
+++ b/libutils/src/main/res/drawable/imageloader_default_cover_bg.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/libutils/src/test/java/com/jiangdg/utils/ExampleUnitTest.kt b/libutils/src/test/java/com/jiangdg/utils/ExampleUnitTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..885ee7b3dc649de4c25b0f8e2c89e5296aa55f03
--- /dev/null
+++ b/libutils/src/test/java/com/jiangdg/utils/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.jiangdg.utils
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/libuvc/.gitignore b/libuvc/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a7d119068b8597e79d24a08076856bbee58ee54b
--- /dev/null
+++ b/libuvc/.gitignore
@@ -0,0 +1,2 @@
+/build
+/src/main/obj
\ No newline at end of file
diff --git a/libuvc/build.gradle b/libuvc/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..4d98d28d9aae207ef2e2996500816eee119815ac
--- /dev/null
+++ b/libuvc/build.gradle
@@ -0,0 +1,86 @@
+apply plugin: 'com.android.library'
+
+import org.apache.tools.ant.taskdefs.condition.Os
+
+android {
+ compileSdkVersion rootProject.ext.versionCompiler
+ buildToolsVersion rootProject.ext.versionBuildTool
+
+ compileOptions {
+ sourceCompatibility rootProject.ext.javaSourceCompatibility
+ targetCompatibility rootProject.ext.javaTargetCompatibility
+ }
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.versionTarget
+ }
+
+ lintOptions {
+ checkReleaseBuilds false
+ // Or, if you prefer, you can continue to check for errors in release builds,
+ // but continue the build even when errors are found:
+ abortOnError false
+ // The demo app does not have translations.
+ disable 'MissingTranslation'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+ sourceSets {
+ main {
+ jniLibs.srcDir 'src/main/libs'
+ jni.srcDirs = []
+ }
+ }
+ repositories {
+ flatDir {
+ dir 'libs'
+ }
+ }
+}
+
+tasks.withType(JavaCompile) {
+ compileTask -> compileTask.dependsOn ndkBuild
+}
+
+String getNdkBuildPath() {
+ def ndkBuildingDir = System.getenv("NDK_HOME")
+ if (ndkBuildingDir==null || ndkBuildingDir.isEmpty()) {
+ Properties properties = new Properties()
+ properties.load(project.rootProject.file('local.properties').newDataInputStream())
+ ndkBuildingDir = properties.getProperty("ndk.dir")
+ }
+ def ndkBuildPath = ndkBuildingDir
+ if (Os.isFamily(Os.FAMILY_WINDOWS)) {
+ ndkBuildPath = ndkBuildingDir + '/ndk-build.cmd'
+ } else {
+ ndkBuildPath = ndkBuildingDir + '/ndk-build'
+ }
+ return ndkBuildPath
+}
+
+task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
+ println('executing ndkBuild')
+ def ndkBuildPath = getNdkBuildPath();
+ commandLine ndkBuildPath, '-j8', '-C', file('src/main').absolutePath
+}
+
+task ndkClean(type: Exec, description: 'clean JNI libraries') {
+ println('executing ndkBuild clean')
+ def ndkBuildPath = getNdkBuildPath();
+ commandLine ndkBuildPath, 'clean', '-C', file('src/main').absolutePath
+}
+
+clean.dependsOn 'ndkClean'
+
+dependencies {
+ implementation fileTree(dir: new File(buildDir, 'libs'), include: '*.jar')
+ implementation "androidx.appcompat:appcompat:${androidXVersion}"
+ implementation 'com.elvishew:xlog:1.11.0'
+ implementation project(path: ':libuvccommon')
+}
diff --git a/libuvc/consumer-rules.pro b/libuvc/consumer-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libuvc/proguard-rules.pro b/libuvc/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/libuvc/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/libuvc/src/main/AndroidManifest.xml b/libuvc/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dc6cba6c967ace9fc4e8257f193083ce1afa75f9
--- /dev/null
+++ b/libuvc/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/libuvc/src/main/java/com/serenegiant/common/BaseActivity.java b/libuvc/src/main/java/com/serenegiant/common/BaseActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..35ad6b093e86b4670368237fd1c1dbffa63a9072
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/common/BaseActivity.java
@@ -0,0 +1,335 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.common;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.serenegiant.dialog.MessageDialogFragmentV4;
+import com.serenegiant.utils.BuildCheck;
+import com.serenegiant.utils.HandlerThreadHandler;
+import com.serenegiant.utils.PermissionCheck;
+
+/**
+ * Created by saki on 2016/11/18.
+ *
+ */
+public class BaseActivity extends AppCompatActivity
+ implements MessageDialogFragmentV4.MessageDialogListener {
+
+ private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
+ private static final String TAG = BaseActivity.class.getSimpleName();
+
+ /** UI操作のためのHandler */
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+ private final Thread mUiThread = mUIHandler.getLooper().getThread();
+ /** ワーカースレッド上で処理するためのHandler */
+ private Handler mWorkerHandler;
+ private long mWorkerThreadID = -1;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // ワーカースレッドを生成
+ if (mWorkerHandler == null) {
+ mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
+ mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ clearToast();
+ super.onPause();
+ }
+
+ @Override
+ protected synchronized void onDestroy() {
+ // ワーカースレッドを破棄
+ if (mWorkerHandler != null) {
+ try {
+ mWorkerHandler.getLooper().quit();
+ } catch (final Exception e) {
+ //
+ }
+ mWorkerHandler = null;
+ }
+ super.onDestroy();
+ }
+
+//================================================================================
+ /**
+ * UIスレッドでRunnableを実行するためのヘルパーメソッド
+ * @param task
+ * @param duration
+ */
+ public final void runOnUiThread(final Runnable task, final long duration) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ if ((duration > 0) || Thread.currentThread() != mUiThread) {
+ mUIHandler.postDelayed(task, duration);
+ } else {
+ try {
+ task.run();
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ /**
+ * UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
+ * @param task
+ */
+ public final void removeFromUiThread(final Runnable task) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ }
+
+ /**
+ * ワーカースレッド上で指定したRunnableを実行する
+ * 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
+ * @param task
+ * @param delayMillis
+ */
+ protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
+ if ((task == null) || (mWorkerHandler == null)) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ if (delayMillis > 0) {
+ mWorkerHandler.postDelayed(task, delayMillis);
+ } else if (mWorkerThreadID == Thread.currentThread().getId()) {
+ task.run();
+ } else {
+ mWorkerHandler.post(task);
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ /**
+ * 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
+ * @param task
+ */
+ protected final synchronized void removeEvent(final Runnable task) {
+ if (task == null) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+//================================================================================
+ private Toast mToast;
+ /**
+ * Toastでメッセージを表示
+ * @param msg
+ */
+ protected void showToast(@StringRes final int msg, final Object... args) {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = new ShowToastTask(msg, args);
+ runOnUiThread(mShowToastTask, 0);
+ }
+
+ /**
+ * Toastが表示されていればキャンセルする
+ */
+ protected void clearToast() {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = null;
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ private ShowToastTask mShowToastTask;
+ private final class ShowToastTask implements Runnable {
+ final int msg;
+ final Object args;
+ private ShowToastTask(@StringRes final int msg, final Object... args) {
+ this.msg = msg;
+ this.args = args;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ final String _msg = (args != null) ? getString(msg, args) : getString(msg);
+ mToast = Toast.makeText(BaseActivity.this, _msg, Toast.LENGTH_SHORT);
+ mToast.show();
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+ }
+
+//================================================================================
+ /**
+ * MessageDialogFragmentメッセージダイアログからのコールバックリスナー
+ * @param dialog
+ * @param requestCode
+ * @param permissions
+ * @param result
+ */
+ @SuppressLint("NewApi")
+ @Override
+ public void onMessageDialogResult(final MessageDialogFragmentV4 dialog, final int requestCode, final String[] permissions, final boolean result) {
+ if (result) {
+ // メッセージダイアログでOKを押された時はパーミッション要求する
+ if (BuildCheck.isMarshmallow()) {
+ requestPermissions(permissions, requestCode);
+ return;
+ }
+ }
+ // メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
+ for (final String permission: permissions) {
+ checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(this, permission));
+ }
+ }
+
+ /**
+ * パーミッション要求結果を受け取るためのメソッド
+ * @param requestCode
+ * @param permissions
+ * @param grantResults
+ */
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
+ final int n = Math.min(permissions.length, grantResults.length);
+ for (int i = 0; i < n; i++) {
+ checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+
+ /**
+ * パーミッション要求の結果をチェック
+ * ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
+ * @param requestCode
+ * @param permission
+ * @param result
+ */
+ protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
+ // パーミッションがないときにはメッセージを表示する
+ if (!result && (permission != null)) {
+ if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
+ showToast(R.string.permission_audio);
+ }
+ if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
+ showToast(R.string.permission_ext_storage);
+ }
+ if (Manifest.permission.INTERNET.equals(permission)) {
+ showToast(R.string.permission_network);
+ }
+ }
+ }
+
+ // 動的パーミッション要求時の要求コード
+ protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
+ protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
+ protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
+ protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
+
+ /**
+ * 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true 外部ストレージへの書き込みパーミッションが有る
+ */
+ protected boolean checkPermissionWriteExternalStorage() {
+ if (!PermissionCheck.hasWriteExternalStorage(this)) {
+ MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
+ R.string.permission_title, R.string.permission_ext_storage_request,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 録音のパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true 録音のパーミッションが有る
+ */
+ protected boolean checkPermissionAudio() {
+ if (!PermissionCheck.hasAudio(this)) {
+ MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
+ R.string.permission_title, R.string.permission_audio_recording_request,
+ new String[]{Manifest.permission.RECORD_AUDIO});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * ネットワークアクセスのパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true ネットワークアクセスのパーミッションが有る
+ */
+ protected boolean checkPermissionNetwork() {
+ if (!PermissionCheck.hasNetwork(this)) {
+ MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_NETWORK,
+ R.string.permission_title, R.string.permission_network_request,
+ new String[]{Manifest.permission.INTERNET});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * カメラアクセスのパーミッションがあるかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true カメラアクセスのパーミッションが有る
+ */
+ protected boolean checkPermissionCamera() {
+ if (!PermissionCheck.hasCamera(this)) {
+ MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_CAMERA,
+ R.string.permission_title, R.string.permission_camera_request,
+ new String[]{Manifest.permission.CAMERA});
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/libuvc/src/main/java/com/serenegiant/common/BaseFragment.java b/libuvc/src/main/java/com/serenegiant/common/BaseFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..932a800339415cf74fec226706c6ab6cb3fface8
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/common/BaseFragment.java
@@ -0,0 +1,342 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.common;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Fragment;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+import com.serenegiant.dialog.MessageDialogFragment;
+import com.serenegiant.utils.BuildCheck;
+import com.serenegiant.utils.HandlerThreadHandler;
+import com.serenegiant.utils.PermissionCheck;
+
+/**
+ * Created by saki on 2016/11/19.
+ *
+ */
+public class BaseFragment extends Fragment
+ implements MessageDialogFragment.MessageDialogListener {
+
+ private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
+ private static final String TAG = BaseFragment.class.getSimpleName();
+
+ /** UI操作のためのHandler */
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+ private final Thread mUiThread = mUIHandler.getLooper().getThread();
+ /** ワーカースレッド上で処理するためのHandler */
+ private Handler mWorkerHandler;
+ private long mWorkerThreadID = -1;
+
+ public BaseFragment() {
+ super();
+ }
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // ワーカースレッドを生成
+ if (mWorkerHandler == null) {
+ mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
+ mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ clearToast();
+ super.onPause();
+ }
+
+ @Override
+ public synchronized void onDestroy() {
+ // ワーカースレッドを破棄
+ if (mWorkerHandler != null) {
+ try {
+ mWorkerHandler.getLooper().quit();
+ } catch (final Exception e) {
+ //
+ }
+ mWorkerHandler = null;
+ }
+ super.onDestroy();
+ }
+
+//================================================================================
+ /**
+ * UIスレッドでRunnableを実行するためのヘルパーメソッド
+ * @param task
+ * @param duration
+ */
+ public final void runOnUiThread(final Runnable task, final long duration) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ if ((duration > 0) || Thread.currentThread() != mUiThread) {
+ mUIHandler.postDelayed(task, duration);
+ } else {
+ try {
+ task.run();
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ /**
+ * UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
+ * @param task
+ */
+ public final void removeFromUiThread(final Runnable task) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ }
+
+ /**
+ * ワーカースレッド上で指定したRunnableを実行する
+ * 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
+ * @param task
+ * @param delayMillis
+ */
+ protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
+ if ((task == null) || (mWorkerHandler == null)) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ if (delayMillis > 0) {
+ mWorkerHandler.postDelayed(task, delayMillis);
+ } else if (mWorkerThreadID == Thread.currentThread().getId()) {
+ task.run();
+ } else {
+ mWorkerHandler.post(task);
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ /**
+ * 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
+ * @param task
+ */
+ protected final synchronized void removeEvent(final Runnable task) {
+ if (task == null) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+//================================================================================
+ private Toast mToast;
+ /**
+ * Toastでメッセージを表示
+ * @param msg
+ */
+ protected void showToast(@StringRes final int msg, final Object... args) {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = new ShowToastTask(msg, args);
+ runOnUiThread(mShowToastTask, 0);
+ }
+
+ /**
+ * Toastが表示されていればキャンセルする
+ */
+ protected void clearToast() {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = null;
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ private ShowToastTask mShowToastTask;
+ private final class ShowToastTask implements Runnable {
+ final int msg;
+ final Object args;
+ private ShowToastTask(@StringRes final int msg, final Object... args) {
+ this.msg = msg;
+ this.args = args;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ if (args != null) {
+ final String _msg = getString(msg, args);
+ mToast = Toast.makeText(getActivity(), _msg, Toast.LENGTH_SHORT);
+ } else {
+ mToast = Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT);
+ }
+ mToast.show();
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+ }
+
+//================================================================================
+ /**
+ * MessageDialogFragmentメッセージダイアログからのコールバックリスナー
+ * @param dialog
+ * @param requestCode
+ * @param permissions
+ * @param result
+ */
+ @SuppressLint("NewApi")
+ @Override
+ public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result) {
+ if (result) {
+ // メッセージダイアログでOKを押された時はパーミッション要求する
+ if (BuildCheck.isMarshmallow()) {
+ requestPermissions(permissions, requestCode);
+ return;
+ }
+ }
+ // メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
+ for (final String permission: permissions) {
+ checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(getActivity(), permission));
+ }
+ }
+
+ /**
+ * パーミッション要求結果を受け取るためのメソッド
+ * @param requestCode
+ * @param permissions
+ * @param grantResults
+ */
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
+ final int n = Math.min(permissions.length, grantResults.length);
+ for (int i = 0; i < n; i++) {
+ checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+
+ /**
+ * パーミッション要求の結果をチェック
+ * ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
+ * @param requestCode
+ * @param permission
+ * @param result
+ */
+ protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
+ // パーミッションがないときにはメッセージを表示する
+ if (!result && (permission != null)) {
+ if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
+ showToast(com.serenegiant.common.R.string.permission_audio);
+ }
+ if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
+ showToast(com.serenegiant.common.R.string.permission_ext_storage);
+ }
+ if (Manifest.permission.INTERNET.equals(permission)) {
+ showToast(com.serenegiant.common.R.string.permission_network);
+ }
+ }
+ }
+
+ // 動的パーミッション要求時の要求コード
+ protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
+ protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
+ protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
+ protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
+
+ /**
+ * 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true 外部ストレージへの書き込みパーミッションが有る
+ */
+ protected boolean checkPermissionWriteExternalStorage() {
+ if (!PermissionCheck.hasWriteExternalStorage(getActivity())) {
+ MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
+ com.serenegiant.common.R.string.permission_title, com.serenegiant.common.R.string.permission_ext_storage_request,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 録音のパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true 録音のパーミッションが有る
+ */
+ protected boolean checkPermissionAudio() {
+ if (!PermissionCheck.hasAudio(getActivity())) {
+ MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
+ com.serenegiant.common.R.string.permission_title, com.serenegiant.common.R.string.permission_audio_recording_request,
+ new String[]{Manifest.permission.RECORD_AUDIO});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * ネットワークアクセスのパーミッションが有るかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true ネットワークアクセスのパーミッションが有る
+ */
+ protected boolean checkPermissionNetwork() {
+ if (!PermissionCheck.hasNetwork(getActivity())) {
+ MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_NETWORK,
+ com.serenegiant.common.R.string.permission_title, com.serenegiant.common.R.string.permission_network_request,
+ new String[]{Manifest.permission.INTERNET});
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * カメラアクセスのパーミッションがあるかどうかをチェック
+ * なければ説明ダイアログを表示する
+ * @return true カメラアクセスのパーミッションが有る
+ */
+ protected boolean checkPermissionCamera() {
+ if (!PermissionCheck.hasCamera(getActivity())) {
+ MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_CAMERA,
+ com.serenegiant.common.R.string.permission_title, com.serenegiant.common.R.string.permission_camera_request,
+ new String[]{Manifest.permission.CAMERA});
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/libuvc/src/main/java/com/serenegiant/common/BaseService.java b/libuvc/src/main/java/com/serenegiant/common/BaseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b93df8d5cde45206eeccedf945ff97d48a51966
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/common/BaseService.java
@@ -0,0 +1,131 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.common;
+
+import android.app.Service;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.serenegiant.utils.HandlerThreadHandler;
+
+public abstract class BaseService extends Service {
+ private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
+ private static final String TAG = BaseService.class.getSimpleName();
+
+ /** UI操作のためのHandler */
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+ private final Thread mUiThread = mUIHandler.getLooper().getThread();
+ /** ワーカースレッド上で処理するためのHandler */
+ private Handler mWorkerHandler;
+ private long mWorkerThreadID = -1;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // ワーカースレッドを生成
+ if (mWorkerHandler == null) {
+ mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
+ mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
+ }
+ }
+
+ @Override
+ public synchronized void onDestroy() {
+ // ワーカースレッドを破棄
+ if (mWorkerHandler != null) {
+ try {
+ mWorkerHandler.getLooper().quit();
+ } catch (final Exception e) {
+ //
+ }
+ mWorkerHandler = null;
+ }
+ super.onDestroy();
+ }
+
+//================================================================================
+ /**
+ * UIスレッドでRunnableを実行するためのヘルパーメソッド
+ * @param task
+ * @param duration
+ */
+ public final void runOnUiThread(final Runnable task, final long duration) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ if ((duration > 0) || Thread.currentThread() != mUiThread) {
+ mUIHandler.postDelayed(task, duration);
+ } else {
+ try {
+ task.run();
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ /**
+ * UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
+ * @param task
+ */
+ public final void removeFromUiThread(final Runnable task) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ }
+
+ /**
+ * ワーカースレッド上で指定したRunnableを実行する
+ * 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
+ * @param task
+ * @param delayMillis
+ */
+ protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
+ if ((task == null) || (mWorkerHandler == null)) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ if (delayMillis > 0) {
+ mWorkerHandler.postDelayed(task, delayMillis);
+ } else if (mWorkerThreadID == Thread.currentThread().getId()) {
+ task.run();
+ } else {
+ mWorkerHandler.post(task);
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ /**
+ * 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
+ * @param task
+ */
+ protected final synchronized void removeEvent(final Runnable task) {
+ if (task == null) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/DeviceFilter.java b/libuvc/src/main/java/com/serenegiant/usb/DeviceFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e94263c922411e225d7eed2774df3650e2f075c
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/DeviceFilter.java
@@ -0,0 +1,527 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.content.Context;
+import android.content.res.Resources.NotFoundException;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class DeviceFilter {
+
+ private static final String TAG = "DeviceFilter";
+
+ // USB Vendor ID (or -1 for unspecified)
+ public final int mVendorId;
+ // USB Product ID (or -1 for unspecified)
+ public final int mProductId;
+ // USB device or interface class (or -1 for unspecified)
+ public final int mClass;
+ // USB device subclass (or -1 for unspecified)
+ public final int mSubclass;
+ // USB device protocol (or -1 for unspecified)
+ public final int mProtocol;
+ // USB device manufacturer name string (or null for unspecified)
+ public final String mManufacturerName;
+ // USB device product name string (or null for unspecified)
+ public final String mProductName;
+ // USB device serial number string (or null for unspecified)
+ public final String mSerialNumber;
+ // set true if specific device(s) should exclude
+ public final boolean isExclude;
+
+ public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
+ final int protocol, final String manufacturer, final String product, final String serialNum) {
+ this(vid, pid, clasz, subclass, protocol, manufacturer, product, serialNum, false);
+ }
+
+ public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
+ final int protocol, final String manufacturer, final String product, final String serialNum, final boolean isExclude) {
+ mVendorId = vid;
+ mProductId = pid;
+ mClass = clasz;
+ mSubclass = subclass;
+ mProtocol = protocol;
+ mManufacturerName = manufacturer;
+ mProductName = product;
+ mSerialNumber = serialNum;
+ this.isExclude = isExclude;
+/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
+ mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
+ }
+
+ public DeviceFilter(final UsbDevice device) {
+ this(device, false);
+ }
+
+ public DeviceFilter(final UsbDevice device, final boolean isExclude) {
+ mVendorId = device.getVendorId();
+ mProductId = device.getProductId();
+ mClass = device.getDeviceClass();
+ mSubclass = device.getDeviceSubclass();
+ mProtocol = device.getDeviceProtocol();
+ mManufacturerName = null; // device.getManufacturerName();
+ mProductName = null; // device.getProductName();
+ mSerialNumber = null; // device.getSerialNumber();
+ this.isExclude = isExclude;
+/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
+ mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
+ }
+
+ /**
+ * 指定したxmlリソースからDeviceFilterリストを生成する
+ * @param context
+ * @param deviceFilterXmlId
+ * @return
+ */
+ public static List getDeviceFilters(final Context context, final int deviceFilterXmlId) {
+ final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId);
+ final List deviceFilters = new ArrayList();
+ try {
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ final DeviceFilter deviceFilter = readEntryOne(context, parser);
+ if (deviceFilter != null) {
+ deviceFilters.add(deviceFilter);
+ }
+ }
+ eventType = parser.next();
+ }
+ } catch (final XmlPullParserException e) {
+ Log.d(TAG, "XmlPullParserException", e);
+ } catch (final IOException e) {
+ Log.d(TAG, "IOException", e);
+ }
+
+ return Collections.unmodifiableList(deviceFilters);
+ }
+
+ /**
+ * read as integer values with default value from xml(w/o exception throws)
+ * resource integer id is also resolved into integer
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final int getAttributeInteger(final Context context, final XmlPullParser parser, final String namespace, final String name, final int defaultValue) {
+ int result = defaultValue;
+ try {
+ String v = parser.getAttributeValue(namespace, name);
+ if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
+ final String r = v.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0) {
+ result = context.getResources().getInteger(resId);
+ }
+ } else {
+ int radix = 10;
+ if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
+ (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ v = v.substring(2);
+ }
+ result = Integer.parseInt(v, radix);
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * read as boolean values with default value from xml(w/o exception throws)
+ * resource boolean id is also resolved into boolean
+ * if the value is zero, return false, if the value is non-zero integer, return true
+ * @param context
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final boolean getAttributeBoolean(final Context context, final XmlPullParser parser, final String namespace, final String name, final boolean defaultValue) {
+ boolean result = defaultValue;
+ try {
+ String v = parser.getAttributeValue(namespace, name);
+ if ("TRUE".equalsIgnoreCase(v)) {
+ result = true;
+ } else if ("FALSE".equalsIgnoreCase(v)) {
+ result = false;
+ } else if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
+ final String r = v.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0) {
+ result = context.getResources().getBoolean(resId);
+ }
+ } else {
+ int radix = 10;
+ if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
+ (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ v = v.substring(2);
+ }
+ final int val = Integer.parseInt(v, radix);
+ result = val != 0;
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * read as String attribute with default value from xml(w/o exception throws)
+ * resource string id is also resolved into string
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final String getAttributeString(final Context context, final XmlPullParser parser, final String namespace, final String name, final String defaultValue) {
+ String result = defaultValue;
+ try {
+ result = parser.getAttributeValue(namespace, name);
+ if (result == null)
+ result = defaultValue;
+ if (!TextUtils.isEmpty(result) && result.startsWith("@")) {
+ final String r = result.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0)
+ result = context.getResources().getString(resId);
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ public static DeviceFilter readEntryOne(final Context context, final XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int vendorId = -1;
+ int productId = -1;
+ int deviceClass = -1;
+ int deviceSubclass = -1;
+ int deviceProtocol = -1;
+ boolean exclude = false;
+ String manufacturerName = null;
+ String productName = null;
+ String serialNumber = null;
+ boolean hasValue = false;
+
+ String tag;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ tag = parser.getName();
+ if (!TextUtils.isEmpty(tag) && (tag.equalsIgnoreCase("usb-device"))) {
+ if (eventType == XmlPullParser.START_TAG) {
+ hasValue = true;
+ vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1);
+ if (vendorId == -1) {
+ vendorId = getAttributeInteger(context, parser, null, "vendorId", -1);
+ if (vendorId == -1)
+ vendorId = getAttributeInteger(context, parser, null, "venderId", -1);
+ }
+ productId = getAttributeInteger(context, parser, null, "product-id", -1);
+ if (productId == -1)
+ productId = getAttributeInteger(context, parser, null, "productId", -1);
+ deviceClass = getAttributeInteger(context, parser, null, "class", -1);
+ deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1);
+ deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1);
+ manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null);
+ if (TextUtils.isEmpty(manufacturerName))
+ manufacturerName = getAttributeString(context, parser, null, "manufacture", null);
+ productName = getAttributeString(context, parser, null, "product-name", null);
+ if (TextUtils.isEmpty(productName))
+ productName = getAttributeString(context, parser, null, "product", null);
+ serialNumber = getAttributeString(context, parser, null, "serial-number", null);
+ if (TextUtils.isEmpty(serialNumber))
+ serialNumber = getAttributeString(context, parser, null, "serial", null);
+ exclude = getAttributeBoolean(context, parser, null, "exclude", false);
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (hasValue) {
+ return new DeviceFilter(vendorId, productId, deviceClass,
+ deviceSubclass, deviceProtocol, manufacturerName, productName,
+ serialNumber, exclude);
+ }
+ }
+ }
+ eventType = parser.next();
+ }
+ return null;
+ }
+
+/* public void write(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "usb-device");
+ if (mVendorId != -1) {
+ serializer
+ .attribute(null, "vendor-id", Integer.toString(mVendorId));
+ }
+ if (mProductId != -1) {
+ serializer.attribute(null, "product-id",
+ Integer.toString(mProductId));
+ }
+ if (mClass != -1) {
+ serializer.attribute(null, "class", Integer.toString(mClass));
+ }
+ if (mSubclass != -1) {
+ serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+ }
+ if (mProtocol != -1) {
+ serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+ }
+ if (mManufacturerName != null) {
+ serializer.attribute(null, "manufacturer-name", mManufacturerName);
+ }
+ if (mProductName != null) {
+ serializer.attribute(null, "product-name", mProductName);
+ }
+ if (mSerialNumber != null) {
+ serializer.attribute(null, "serial-number", mSerialNumber);
+ }
+ serializer.attribute(null, "serial-number", Boolean.toString(isExclude));
+ serializer.endTag(null, "usb-device");
+ } */
+
+ /**
+ * 指定したクラス・サブクラス・プロトコルがこのDeviceFilterとマッチするかどうかを返す
+ * mExcludeフラグは別途#isExcludeか自前でチェックすること
+ * @param clasz
+ * @param subclass
+ * @param protocol
+ * @return
+ */
+ private boolean matches(final int clasz, final int subclass, final int protocol) {
+ return ((mClass == -1 || clasz == mClass)
+ && (mSubclass == -1 || subclass == mSubclass) && (mProtocol == -1 || protocol == mProtocol));
+ }
+
+ /**
+ * 指定したUsbDeviceがこのDeviceFilterにマッチするかどうかを返す
+ * mExcludeフラグは別途#isExcludeか自前でチェックすること
+ * @param device
+ * @return
+ */
+ public boolean matches(final UsbDevice device) {
+ if (mVendorId != -1 && device.getVendorId() != mVendorId) {
+ return false;
+ }
+ if (mProductId != -1 && device.getProductId() != mProductId) {
+ return false;
+ }
+/* if (mManufacturerName != null && device.getManufacturerName() == null)
+ return false;
+ if (mProductName != null && device.getProductName() == null)
+ return false;
+ if (mSerialNumber != null && device.getSerialNumber() == null)
+ return false;
+ if (mManufacturerName != null && device.getManufacturerName() != null
+ && !mManufacturerName.equals(device.getManufacturerName()))
+ return false;
+ if (mProductName != null && device.getProductName() != null
+ && !mProductName.equals(device.getProductName()))
+ return false;
+ if (mSerialNumber != null && device.getSerialNumber() != null
+ && !mSerialNumber.equals(device.getSerialNumber()))
+ return false; */
+
+ // check device class/subclass/protocol
+ if (matches(device.getDeviceClass(), device.getDeviceSubclass(), device.getDeviceProtocol())) {
+ return true;
+ }
+
+ // if device doesn't match, check the interfaces
+ final int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ final UsbInterface intf = device.getInterface(i);
+ if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), intf.getInterfaceProtocol())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * このDeviceFilterに一致してかつmExcludeがtrueならtrueを返す
+ * @param device
+ * @return
+ */
+ public boolean isExclude(final UsbDevice device) {
+ return isExclude && matches(device);
+ }
+
+ /**
+ * これって要らんかも, equalsでできる気が
+ * @param f
+ * @return
+ */
+ public boolean matches(final DeviceFilter f) {
+ if (isExclude != f.isExclude) {
+ return false;
+ }
+ if (mVendorId != -1 && f.mVendorId != mVendorId) {
+ return false;
+ }
+ if (mProductId != -1 && f.mProductId != mProductId) {
+ return false;
+ }
+ if (f.mManufacturerName != null && mManufacturerName == null) {
+ return false;
+ }
+ if (f.mProductName != null && mProductName == null) {
+ return false;
+ }
+ if (f.mSerialNumber != null && mSerialNumber == null) {
+ return false;
+ }
+ if (mManufacturerName != null && f.mManufacturerName != null
+ && !mManufacturerName.equals(f.mManufacturerName)) {
+ return false;
+ }
+ if (mProductName != null && f.mProductName != null
+ && !mProductName.equals(f.mProductName)) {
+ return false;
+ }
+ if (mSerialNumber != null && f.mSerialNumber != null
+ && !mSerialNumber.equals(f.mSerialNumber)) {
+ return false;
+ }
+
+ // check device class/subclass/protocol
+ return matches(f.mClass, f.mSubclass, f.mProtocol);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ // can't compare if we have wildcard strings
+ if (mVendorId == -1 || mProductId == -1 || mClass == -1
+ || mSubclass == -1 || mProtocol == -1) {
+ return false;
+ }
+ if (obj instanceof DeviceFilter) {
+ final DeviceFilter filter = (DeviceFilter) obj;
+
+ if (filter.mVendorId != mVendorId
+ || filter.mProductId != mProductId
+ || filter.mClass != mClass || filter.mSubclass != mSubclass
+ || filter.mProtocol != mProtocol) {
+ return false;
+ }
+ if ((filter.mManufacturerName != null && mManufacturerName == null)
+ || (filter.mManufacturerName == null && mManufacturerName != null)
+ || (filter.mProductName != null && mProductName == null)
+ || (filter.mProductName == null && mProductName != null)
+ || (filter.mSerialNumber != null && mSerialNumber == null)
+ || (filter.mSerialNumber == null && mSerialNumber != null)) {
+ return false;
+ }
+ if ((filter.mManufacturerName != null && mManufacturerName != null && !mManufacturerName
+ .equals(filter.mManufacturerName))
+ || (filter.mProductName != null && mProductName != null && !mProductName
+ .equals(filter.mProductName))
+ || (filter.mSerialNumber != null && mSerialNumber != null && !mSerialNumber
+ .equals(filter.mSerialNumber))) {
+ return false;
+ }
+ return (filter.isExclude != isExclude);
+ }
+ if (obj instanceof UsbDevice) {
+ final UsbDevice device = (UsbDevice) obj;
+ if (isExclude
+ || (device.getVendorId() != mVendorId)
+ || (device.getProductId() != mProductId)
+ || (device.getDeviceClass() != mClass)
+ || (device.getDeviceSubclass() != mSubclass)
+ || (device.getDeviceProtocol() != mProtocol) ) {
+ return false;
+ }
+/* if ((mManufacturerName != null && device.getManufacturerName() == null)
+ || (mManufacturerName == null && device
+ .getManufacturerName() != null)
+ || (mProductName != null && device.getProductName() == null)
+ || (mProductName == null && device.getProductName() != null)
+ || (mSerialNumber != null && device.getSerialNumber() == null)
+ || (mSerialNumber == null && device.getSerialNumber() != null)) {
+ return (false);
+ } */
+/* if ((device.getManufacturerName() != null && !mManufacturerName
+ .equals(device.getManufacturerName()))
+ || (device.getProductName() != null && !mProductName
+ .equals(device.getProductName()))
+ || (device.getSerialNumber() != null && !mSerialNumber
+ .equals(device.getSerialNumber()))) {
+ return (false);
+ } */
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (((mVendorId << 16) | mProductId) ^ ((mClass << 16)
+ | (mSubclass << 8) | mProtocol));
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId="
+ + mProductId + ",mClass=" + mClass + ",mSubclass=" + mSubclass
+ + ",mProtocol=" + mProtocol
+ + ",mManufacturerName=" + mManufacturerName
+ + ",mProductName=" + mProductName
+ + ",mSerialNumber=" + mSerialNumber
+ + ",isExclude=" + isExclude
+ + "]";
+ }
+
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/IButtonCallback.java b/libuvc/src/main/java/com/serenegiant/usb/IButtonCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..4025085c4604555ddb75452685b108bba5f9711d
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/IButtonCallback.java
@@ -0,0 +1,5 @@
+package com.serenegiant.usb;
+
+public interface IButtonCallback {
+ void onButton(int button, int state);
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/IFrameCallback.java b/libuvc/src/main/java/com/serenegiant/usb/IFrameCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6aee755e070785e5dd0f0f313cdd745b4297ab1
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/IFrameCallback.java
@@ -0,0 +1,46 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Callback interface for UVCCamera class
+ * If you need frame data as ByteBuffer, you can use this callback interface with UVCCamera#setFrameCallback
+ */
+public interface IFrameCallback {
+ /**
+ * This method is called from native library via JNI on the same thread as UVCCamera#startCapture.
+ * You can use both UVCCamera#startCapture and #setFrameCallback
+ * but it is better to use either for better performance.
+ * You can also pass pixel format type to UVCCamera#setFrameCallback for this method.
+ * Some frames may drops if this method takes a time.
+ * When you use some color format like NV21, this library never execute color space conversion,
+ * just execute pixel format conversion. If you want to get same result as on screen, please try to
+ * consider to get images via texture(SurfaceTexture) and read pixel buffer from it using OpenGL|ES2/3
+ * instead of using IFrameCallback(this way is much efficient in most case than using IFrameCallback).
+ * @param frame this is direct ByteBuffer from JNI layer and you should handle it's byte order and limitation.
+ */
+ public void onFrame(ByteBuffer frame);
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/IStatusCallback.java b/libuvc/src/main/java/com/serenegiant/usb/IStatusCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad743204ffee7f2f7e2df8c582c9147023fd9336
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/IStatusCallback.java
@@ -0,0 +1,7 @@
+package com.serenegiant.usb;
+
+import java.nio.ByteBuffer;
+
+public interface IStatusCallback {
+ void onStatus(int statusClass, int event, int selector, int statusAttribute, ByteBuffer data);
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/Size.java b/libuvc/src/main/java/com/serenegiant/usb/Size.java
new file mode 100644
index 0000000000000000000000000000000000000000..963a805398554bef88ce9264b8ea839f45bc181b
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/Size.java
@@ -0,0 +1,302 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+public class Size implements Parcelable {
+ //
+ /**
+ * native側のuvc_raw_format_tの値, こっちは主にlibuvc用
+ * 9999 is still image
+ */
+ public int type;
+ /**
+ * native側のraw_frame_tの値, androusb用,
+ * libuvcは対応していない
+ */
+ public int frame_type;
+ public int index;
+ public int width;
+ public int height;
+ public int frameIntervalType;
+ public int frameIntervalIndex;
+ public int[] intervals;
+ // ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
+ public float[] fps;
+ private String frameRates;
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ frameIntervalType = -1;
+ frameIntervalIndex = 0;
+ intervals = null;
+ updateFrameRate();
+ }
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ * @param _min_intervals
+ * @param _max_intervals
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int _min_intervals, final int _max_intervals, final int _step) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ frameIntervalType = 0;
+ frameIntervalIndex = 0;
+ intervals = new int[3];
+ intervals[0] = _min_intervals;
+ intervals[1] = _max_intervals;
+ intervals[2] = _step;
+ updateFrameRate();
+ }
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ * @param _intervals
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ final int n = _intervals != null ? _intervals.length : -1;
+ if (n > 0) {
+ frameIntervalType = n;
+ intervals = new int[n];
+ System.arraycopy(_intervals, 0, intervals, 0, n);
+ } else {
+ frameIntervalType = -1;
+ intervals = null;
+ }
+ frameIntervalIndex = 0;
+ updateFrameRate();
+ }
+
+ /**
+ * コピーコンストラクタ
+ * @param other
+ */
+ public Size(final Size other) {
+ type = other.type;
+ frame_type = other.frame_type;
+ index = other.index;
+ width = other.width;
+ height = other.height;
+ frameIntervalType = other.frameIntervalType;
+ frameIntervalIndex = other.frameIntervalIndex;
+ final int n = other.intervals != null ? other.intervals.length : -1;
+ if (n > 0) {
+ intervals = new int[n];
+ System.arraycopy(other.intervals, 0, intervals, 0, n);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+
+ private Size(final Parcel source) {
+ // 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
+ type = source.readInt();
+ frame_type = source.readInt();
+ index = source.readInt();
+ width = source.readInt();
+ height = source.readInt();
+ frameIntervalType = source.readInt();
+ frameIntervalIndex = source.readInt();
+ if (frameIntervalType >= 0) {
+ if (frameIntervalType > 0) {
+ intervals = new int[frameIntervalType];
+ } else {
+ intervals = new int[3];
+ }
+ source.readIntArray(intervals);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+
+ public Size set(final Size other) {
+ if (other != null) {
+ type = other.type;
+ frame_type = other.frame_type;
+ index = other.index;
+ width = other.width;
+ height = other.height;
+ frameIntervalType = other.frameIntervalType;
+ frameIntervalIndex = other.frameIntervalIndex;
+ final int n = other.intervals != null ? other.intervals.length : -1;
+ if (n > 0) {
+ intervals = new int[n];
+ System.arraycopy(other.intervals, 0, intervals, 0, n);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+ return this;
+ }
+
+ public float getCurrentFrameRate() throws IllegalStateException {
+ final int n = fps != null ? fps.length : 0;
+ if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
+ return fps[frameIntervalIndex];
+ }
+ throw new IllegalStateException("unknown frame rate or not ready");
+ }
+
+ public void setCurrentFrameRate(final float frameRate) {
+ // 一番近いのを選ぶ
+ int index = -1;
+ final int n = fps != null ? fps.length : 0;
+ for (int i = 0; i < n; i++) {
+ if (fps[i] <= frameRate) {
+ index = i;
+ break;
+ }
+ }
+ frameIntervalIndex = index;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeInt(type);
+ dest.writeInt(frame_type);
+ dest.writeInt(index);
+ dest.writeInt(width);
+ dest.writeInt(height);
+ dest.writeInt(frameIntervalType);
+ dest.writeInt(frameIntervalIndex);
+ if (intervals != null) {
+ dest.writeIntArray(intervals);
+ }
+ }
+
+ public void updateFrameRate() {
+ final int n = frameIntervalType;
+ if (n > 0) {
+ fps = new float[n];
+ for (int i = 0; i < n; i++) {
+ final float _fps = fps[i] = 10000000.0f / intervals[i];
+ }
+ } else if (n == 0) {
+ try {
+ final int min = Math.min(intervals[0], intervals[1]);
+ final int max = Math.max(intervals[0], intervals[1]);
+ final int step = intervals[2];
+ if (step > 0) {
+ int m = 0;
+ for (int i = min; i <= max; i+= step) { m++; }
+ fps = new float[m];
+ m = 0;
+ for (int i = min; i <= max; i+= step) {
+ final float _fps = fps[m++] = 10000000.0f / i;
+ }
+ } else {
+ final float max_fps = 10000000.0f / min;
+ int m = 0;
+ for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) { m++; }
+ fps = new float[m];
+ m = 0;
+ for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
+ this.fps[m++] = fps;
+ }
+ }
+ } catch (final Exception e) {
+ // ignore, なんでかminとmaxが0になってるんちゃうかな
+ fps = null;
+ }
+ }
+ final int m = fps != null ? fps.length : 0;
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0; i < m; i++) {
+ sb.append(String.format(Locale.US, "%4.1f", fps[i]));
+ if (i < m-1) {
+ sb.append(",");
+ }
+ }
+ sb.append("]");
+ frameRates = sb.toString();
+ if (frameIntervalIndex > m) {
+ frameIntervalIndex = 0;
+ }
+ }
+
+ @Override
+ public String toString() {
+ float frame_rate = 0.0f;
+ try {
+ frame_rate = getCurrentFrameRate();
+ } catch (final Exception e) {
+ }
+ return String.format(Locale.US, "Size(%dx%d@%4.1f,type:%d,frame:%d,index:%d,%s)", width, height, frame_rate, type, frame_type, index, frameRates);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public Size createFromParcel(final Parcel source) {
+ return new Size(source);
+ }
+ @Override
+ public Size[] newArray(final int size) {
+ return new Size[size];
+ }
+ };
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/USBMonitor.java b/libuvc/src/main/java/com/serenegiant/usb/USBMonitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7159b97b371fa7ac2fbbf50f611d333d4facf241
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/USBMonitor.java
@@ -0,0 +1,1380 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Build;
+import android.os.Handler;
+import android.text.TextUtils;
+import com.serenegiant.utils.XLogWrapper;
+import android.util.SparseArray;
+
+import androidx.annotation.RequiresApi;
+
+import com.serenegiant.utils.BuildCheck;
+import com.serenegiant.utils.HandlerThreadHandler;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class USBMonitor {
+
+ public static boolean DEBUG = false; // TODO set false on production
+ private static final String TAG = "USBMonitor";
+
+ private static final String ACTION_USB_PERMISSION_BASE = "com.serenegiant.USB_PERMISSION.";
+ private final String ACTION_USB_PERMISSION = ACTION_USB_PERMISSION_BASE + hashCode();
+
+ public static final String ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
+
+ /**
+ * openしているUsbControlBlock
+ */
+ private final ConcurrentHashMap mCtrlBlocks = new ConcurrentHashMap();
+ private final SparseArray> mHasPermissions = new SparseArray>();
+
+ private final WeakReference mWeakContext;
+ private final UsbManager mUsbManager;
+ private final OnDeviceConnectListener mOnDeviceConnectListener;
+ private PendingIntent mPermissionIntent = null;
+ private List mDeviceFilters = new ArrayList();
+
+ /**
+ * コールバックをワーカースレッドで呼び出すためのハンドラー
+ */
+ private final Handler mAsyncHandler;
+ private volatile boolean destroyed;
+ /**
+ * USB機器の状態変更時のコールバックリスナー
+ */
+ public interface OnDeviceConnectListener {
+ /**
+ * called when device attached
+ * @param device
+ */
+ public void onAttach(UsbDevice device);
+ /**
+ * called when device dettach(after onDisconnect)
+ * @param device
+ */
+ public void onDetach(UsbDevice device);
+ /**
+ * called after device opend
+ * @param device
+ * @param ctrlBlock
+ * @param createNew
+ */
+ public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew);
+ /**
+ * called when USB device removed or its power off (this callback is called after device closing)
+ * @param device
+ * @param ctrlBlock
+ */
+ public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock);
+ /**
+ * called when canceled or could not get permission from user
+ * @param device
+ */
+ public void onCancel(UsbDevice device);
+ }
+
+ public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
+ if (DEBUG) XLogWrapper.v(TAG, "USBMonitor:Constructor");
+ if (listener == null)
+ throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
+ mWeakContext = new WeakReference(context);
+ mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+ mOnDeviceConnectListener = listener;
+ mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
+ destroyed = false;
+ if (DEBUG) XLogWrapper.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
+ }
+
+ /**
+ * Release all related resources,
+ * never reuse again
+ */
+ public void destroy() {
+ if (DEBUG) XLogWrapper.i(TAG, "destroy:");
+ unregister();
+ if (!destroyed) {
+ destroyed = true;
+ // モニターしているUSB機器を全てcloseする
+ final Set keys = mCtrlBlocks.keySet();
+ if (keys != null) {
+ UsbControlBlock ctrlBlock;
+ try {
+ for (final UsbDevice key: keys) {
+ ctrlBlock = mCtrlBlocks.remove(key);
+ if (ctrlBlock != null) {
+ ctrlBlock.close();
+ }
+ }
+ } catch (final Exception e) {
+ XLogWrapper.e(TAG, "destroy:", e);
+ }
+ }
+ mCtrlBlocks.clear();
+ try {
+ mAsyncHandler.getLooper().quit();
+ } catch (final Exception e) {
+ XLogWrapper.e(TAG, "destroy:", e);
+ }
+ }
+ }
+
+ /**
+ * register BroadcastReceiver to monitor USB events
+ * @throws IllegalStateException
+ */
+ @SuppressLint("UnspecifiedImmutableFlag")
+ public synchronized void register() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ if (mPermissionIntent == null) {
+ if (DEBUG) XLogWrapper.i(TAG, "register:");
+ final Context context = mWeakContext.get();
+ if (context != null) {
+ if (Build.VERSION.SDK_INT >= 31) {
+ mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
+ }
+ final IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
+ // ACTION_USB_DEVICE_ATTACHED never comes on some devices so it should not be added here
+ filter.addAction(ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ context.registerReceiver(mUsbReceiver, filter);
+ }
+ // start connection check
+ mDeviceCounts = 0;
+ mAsyncHandler.postDelayed(mDeviceCheckRunnable, 1000);
+ }
+ }
+
+ /**
+ * unregister BroadcastReceiver
+ * @throws IllegalStateException
+ */
+ public synchronized void unregister() throws IllegalStateException {
+ // 接続チェック用Runnableを削除
+ mDeviceCounts = 0;
+ if (!destroyed) {
+ mAsyncHandler.removeCallbacks(mDeviceCheckRunnable);
+ }
+ if (mPermissionIntent != null) {
+// if (DEBUG) XLogWrapper.i(TAG, "unregister:");
+ final Context context = mWeakContext.get();
+ try {
+ if (context != null) {
+ context.unregisterReceiver(mUsbReceiver);
+ }
+ } catch (final Exception e) {
+ XLogWrapper.w(TAG, e);
+ }
+ mPermissionIntent = null;
+ }
+ }
+
+ public synchronized boolean isRegistered() {
+ return !destroyed && (mPermissionIntent != null);
+ }
+
+ /**
+ * set device filter
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.clear();
+ mDeviceFilters.add(filter);
+ }
+
+ /**
+ * デバイスフィルターを追加
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.add(filter);
+ }
+
+ /**
+ * デバイスフィルターを削除
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.remove(filter);
+ }
+
+ /**
+ * set device filters
+ * @param filters
+ * @throws IllegalStateException
+ */
+ public void setDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.clear();
+ mDeviceFilters.addAll(filters);
+ }
+
+ /**
+ * add device filters
+ * @param filters
+ * @throws IllegalStateException
+ */
+ public void addDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.addAll(filters);
+ }
+
+ /**
+ * remove device filters
+ * @param filters
+ */
+ public void removeDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.removeAll(filters);
+ }
+
+ /**
+ * return the number of connected USB devices that matched device filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceCount() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return getDeviceList().size();
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return getDeviceList(mDeviceFilters);
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @param filters
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ // get detected devices
+ final HashMap deviceList = mUsbManager.getDeviceList();
+ final List result = new ArrayList();
+ if (deviceList != null) {
+ if ((filters == null) || filters.isEmpty()) {
+ result.addAll(deviceList.values());
+ } else {
+ for (final UsbDevice device: deviceList.values() ) {
+ // match devices
+ for (final DeviceFilter filter: filters) {
+ if ((filter != null) && filter.matches(device) || (filter != null && filter.mSubclass == device.getDeviceSubclass())) {
+ // when filter matches
+ if (!filter.isExclude) {
+ result.add(device);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @param filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ final HashMap deviceList = mUsbManager.getDeviceList();
+ final List result = new ArrayList();
+ if (deviceList != null) {
+ for (final UsbDevice device: deviceList.values() ) {
+ if ((filter == null) || (filter.matches(device) && !filter.isExclude)) {
+ result.add(device);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * get USB device list, without filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public Iterator getDevices() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ Iterator iterator = null;
+ final HashMap list = mUsbManager.getDeviceList();
+ if (list != null)
+ iterator = list.values().iterator();
+ return iterator;
+ }
+
+ /**
+ * output device list to XLogWrapperCat
+ */
+ public final void dumpDevices() {
+ final HashMap list = mUsbManager.getDeviceList();
+ if (list != null) {
+ final Set keys = list.keySet();
+ if (keys != null && keys.size() > 0) {
+ final StringBuilder sb = new StringBuilder();
+ for (final String key: keys) {
+ final UsbDevice device = list.get(key);
+ final int num_interface = device != null ? device.getInterfaceCount() : 0;
+ sb.setLength(0);
+ for (int i = 0; i < num_interface; i++) {
+ sb.append(String.format(Locale.US, "interface%d:%s", i, device.getInterface(i).toString()));
+ }
+ XLogWrapper.i(TAG, "key=" + key + ":" + device + ":" + sb.toString());
+ }
+ } else {
+ XLogWrapper.i(TAG, "no device");
+ }
+ } else {
+ XLogWrapper.i(TAG, "no device");
+ }
+ }
+
+ /**
+ * return whether the specific Usb device has permission
+ * @param device
+ * @return true: 指定したUsbDeviceにパーミッションがある
+ * @throws IllegalStateException
+ */
+ public final boolean hasPermission(final UsbDevice device) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return updatePermission(device, device != null && mUsbManager.hasPermission(device));
+ }
+
+ /**
+ * 内部で保持しているパーミッション状態を更新
+ * @param device
+ * @param hasPermission
+ * @return hasPermission
+ */
+ private boolean updatePermission(final UsbDevice device, final boolean hasPermission) {
+ // fix api >= 29, permission SecurityException
+ try {
+ final int deviceKey = getDeviceKey(device, true);
+ synchronized (mHasPermissions) {
+ if (hasPermission) {
+ if (mHasPermissions.get(deviceKey) == null) {
+ mHasPermissions.put(deviceKey, new WeakReference(device));
+ }
+ } else {
+ mHasPermissions.remove(deviceKey);
+ }
+ }
+ } catch (SecurityException e) {
+ XLogWrapper.w("jiangdg", e.getLocalizedMessage());
+ }
+
+ return hasPermission;
+ }
+
+ /**
+ * request permission to access to USB device
+ * @param device
+ * @return true if fail to request permission
+ */
+ public synchronized boolean requestPermission(final UsbDevice device) {
+// if (DEBUG) XLogWrapper.v(TAG, "requestPermission:device=" + device);
+ boolean result = false;
+ if (isRegistered()) {
+ if (device != null) {
+ if (DEBUG) XLogWrapper.i(TAG,"request permission, has permission: " + mUsbManager.hasPermission(device));
+ if (mUsbManager.hasPermission(device)) {
+ // call onConnect if app already has permission
+ processConnect(device);
+ } else {
+ try {
+ // パーミッションがなければ要求する
+ if (DEBUG) XLogWrapper.i(TAG, "start request permission...");
+ mUsbManager.requestPermission(device, mPermissionIntent);
+ } catch (final Exception e) {
+ // Android5.1.xのGALAXY系でandroid.permission.sec.MDM_APP_MGMTという意味不明の例外生成するみたい
+ XLogWrapper.w(TAG,"request permission failed, e = " + e.getLocalizedMessage() ,e);
+ processCancel(device);
+ result = true;
+ }
+ }
+ } else {
+ if (DEBUG)
+ XLogWrapper.w(TAG,"request permission failed, device is null?");
+ processCancel(device);
+ result = true;
+ }
+ } else {
+ if (DEBUG)
+ XLogWrapper.w(TAG,"request permission failed, not registered?");
+ processCancel(device);
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * 指定したUsbDeviceをopenする
+ * @param device
+ * @return
+ * @throws SecurityException パーミッションがなければSecurityExceptionを投げる
+ */
+ public UsbControlBlock openDevice(final UsbDevice device) throws SecurityException {
+ if (hasPermission(device)) {
+ UsbControlBlock result = mCtrlBlocks.get(device);
+ if (result == null) {
+ result = new UsbControlBlock(USBMonitor.this, device); // この中でopenDeviceする
+ mCtrlBlocks.put(device, result);
+ }
+ return result;
+ } else {
+ throw new SecurityException("has no permission");
+ }
+ }
+
+ /**
+ * BroadcastReceiver for USB permission
+ */
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (destroyed) return;
+ final String action = intent.getAction();
+ if (ACTION_USB_PERMISSION.equals(action)) {
+ // when received the result of requesting USB permission
+ synchronized (USBMonitor.this) {
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ if (device != null) {
+ // get permission, call onConnect
+ if (DEBUG)
+ XLogWrapper.w(TAG, "get permission success in mUsbReceiver");
+ processConnect(device);
+ }
+ } else {
+ // failed to get permission
+ if (DEBUG)
+ XLogWrapper.w(TAG, "get permission failed in mUsbReceiver");
+ processCancel(device);
+ }
+ }
+ } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ updatePermission(device, hasPermission(device));
+ processAttach(device);
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ // when device removed
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ UsbControlBlock ctrlBlock = mCtrlBlocks.remove(device);
+ if (ctrlBlock != null) {
+ // cleanup
+ ctrlBlock.close();
+ }
+ mDeviceCounts = 0;
+ processDettach(device);
+ }
+ }
+ }
+ };
+
+ /** number of connected & detected devices */
+ private volatile int mDeviceCounts = 0;
+ /**
+ * periodically check connected devices and if it changed, call onAttach
+ */
+ private final Runnable mDeviceCheckRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (destroyed) return;
+ final List devices = getDeviceList();
+ final int n = devices.size();
+ final int hasPermissionCounts;
+ final int m;
+ synchronized (mHasPermissions) {
+ hasPermissionCounts = mHasPermissions.size();
+ mHasPermissions.clear();
+ for (final UsbDevice device: devices) {
+ hasPermission(device);
+ }
+ m = mHasPermissions.size();
+ }
+ if ((n > mDeviceCounts) || (m > hasPermissionCounts)) {
+ mDeviceCounts = n;
+ if (mOnDeviceConnectListener != null) {
+ for (int i = 0; i < n; i++) {
+ final UsbDevice device = devices.get(i);
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onAttach(device);
+ }
+ });
+ }
+ }
+ }
+ mAsyncHandler.postDelayed(this, 2000); // confirm every 2 seconds
+ }
+ };
+
+ /**
+ * open specific USB device
+ * @param device
+ */
+ private final void processConnect(final UsbDevice device) {
+ if (destroyed) return;
+ updatePermission(device, true);
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) XLogWrapper.v(TAG, "processConnect:device=" + device);
+ UsbControlBlock ctrlBlock;
+ final boolean createNew;
+ ctrlBlock = mCtrlBlocks.get(device);
+ if (ctrlBlock == null) {
+ ctrlBlock = new UsbControlBlock(USBMonitor.this, device);
+ mCtrlBlocks.put(device, ctrlBlock);
+ createNew = true;
+ } else {
+ createNew = false;
+ }
+ if (mOnDeviceConnectListener != null) {
+ mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
+ }
+ }
+ });
+ }
+
+ private final void processCancel(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) XLogWrapper.v(TAG, "processCancel:");
+ updatePermission(device, false);
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onCancel(device);
+ }
+ });
+ }
+ }
+
+ private final void processAttach(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) XLogWrapper.v(TAG, "processAttach:");
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onAttach(device);
+ }
+ });
+ }
+ }
+
+ private final void processDettach(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) XLogWrapper.v(TAG, "processDettach:");
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onDetach(device);
+ }
+ });
+ }
+ }
+
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * 同種の製品だと同じキー名になるので注意
+ * @param device nullなら空文字列を返す
+ * @return
+ */
+ public static final String getDeviceKeyName(final UsbDevice device) {
+ return getDeviceKeyName(device, null, false);
+ }
+
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。
+ * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device
+ * @param useNewAPI
+ * @return
+ */
+ public static final String getDeviceKeyName(final UsbDevice device, final boolean useNewAPI) {
+ return getDeviceKeyName(device, null, useNewAPI);
+ }
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。この機器名をHashMapのキーにする
+ * UsbDeviceがopenしている時のみ有効
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * serialがnullや空文字でなければserialを含めたデバイスキー名を生成する
+ * useNewAPI=trueでAPIレベルを満たしていればマニュファクチャ名, バージョン, コンフィギュレーションカウントも使う
+ * @param device nullなら空文字列を返す
+ * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
+ * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
+ * @return
+ */
+ @SuppressLint("NewApi")
+ public static final String getDeviceKeyName(final UsbDevice device, final String serial, final boolean useNewAPI) {
+ if (device == null) return "";
+ final StringBuilder sb = new StringBuilder();
+ sb.append(device.getVendorId()); sb.append("#"); // API >= 12
+ sb.append(device.getProductId()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceClass()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceSubclass()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceProtocol()); // API >= 12
+ if (!TextUtils.isEmpty(serial)) {
+ sb.append("#"); sb.append(serial);
+ }
+ if (useNewAPI && BuildCheck.isAndroid5()) {
+ sb.append("#");
+ if (TextUtils.isEmpty(serial)) {
+ try { sb.append(device.getSerialNumber()); sb.append("#"); } // API >= 21 & targetSdkVersion has to be <= 28
+ catch(SecurityException ignore) {}
+ }
+ sb.append(device.getManufacturerName()); sb.append("#"); // API >= 21
+ sb.append(device.getConfigurationCount()); sb.append("#"); // API >= 21
+ if (BuildCheck.isMarshmallow()) {
+ sb.append(device.getVersion()); sb.append("#"); // API >= 23
+ }
+ }
+// if (DEBUG) XLogWrapper.v(TAG, "getDeviceKeyName:" + sb.toString());
+ return sb.toString();
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * 同種の製品だと同じデバイスキーになるので注意
+ * @param device nullなら0を返す
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device) {
+ return device != null ? getDeviceKeyName(device, null, false).hashCode() : 0;
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device
+ * @param useNewAPI
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device, final boolean useNewAPI) {
+ return device != null ? getDeviceKeyName(device, null, useNewAPI).hashCode() : 0;
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * serialがnullでuseNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device nullなら0を返す
+ * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
+ * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device, final String serial, final boolean useNewAPI) {
+ return device != null ? getDeviceKeyName(device, serial, useNewAPI).hashCode() : 0;
+ }
+
+ public static class UsbDeviceInfo {
+ public String usb_version;
+ public String manufacturer;
+ public String product;
+ public String version;
+ public String serial;
+
+ private void clear() {
+ usb_version = manufacturer = product = version = serial = null;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("UsbDevice:usb_version=%s,manufacturer=%s,product=%s,version=%s,serial=%s",
+ usb_version != null ? usb_version : "",
+ manufacturer != null ? manufacturer : "",
+ product != null ? product : "",
+ version != null ? version : "",
+ serial != null ? serial : "");
+ }
+ }
+
+ private static final int USB_DIR_OUT = 0;
+ private static final int USB_DIR_IN = 0x80;
+ private static final int USB_TYPE_MASK = (0x03 << 5);
+ private static final int USB_TYPE_STANDARD = (0x00 << 5);
+ private static final int USB_TYPE_CLASS = (0x01 << 5);
+ private static final int USB_TYPE_VENDOR = (0x02 << 5);
+ private static final int USB_TYPE_RESERVED = (0x03 << 5);
+ private static final int USB_RECIP_MASK = 0x1f;
+ private static final int USB_RECIP_DEVICE = 0x00;
+ private static final int USB_RECIP_INTERFACE = 0x01;
+ private static final int USB_RECIP_ENDPOINT = 0x02;
+ private static final int USB_RECIP_OTHER = 0x03;
+ private static final int USB_RECIP_PORT = 0x04;
+ private static final int USB_RECIP_RPIPE = 0x05;
+ private static final int USB_REQ_GET_STATUS = 0x00;
+ private static final int USB_REQ_CLEAR_FEATURE = 0x01;
+ private static final int USB_REQ_SET_FEATURE = 0x03;
+ private static final int USB_REQ_SET_ADDRESS = 0x05;
+ private static final int USB_REQ_GET_DESCRIPTOR = 0x06;
+ private static final int USB_REQ_SET_DESCRIPTOR = 0x07;
+ private static final int USB_REQ_GET_CONFIGURATION = 0x08;
+ private static final int USB_REQ_SET_CONFIGURATION = 0x09;
+ private static final int USB_REQ_GET_INTERFACE = 0x0A;
+ private static final int USB_REQ_SET_INTERFACE = 0x0B;
+ private static final int USB_REQ_SYNCH_FRAME = 0x0C;
+ private static final int USB_REQ_SET_SEL = 0x30;
+ private static final int USB_REQ_SET_ISOCH_DELAY = 0x31;
+ private static final int USB_REQ_SET_ENCRYPTION = 0x0D;
+ private static final int USB_REQ_GET_ENCRYPTION = 0x0E;
+ private static final int USB_REQ_RPIPE_ABORT = 0x0E;
+ private static final int USB_REQ_SET_HANDSHAKE = 0x0F;
+ private static final int USB_REQ_RPIPE_RESET = 0x0F;
+ private static final int USB_REQ_GET_HANDSHAKE = 0x10;
+ private static final int USB_REQ_SET_CONNECTION = 0x11;
+ private static final int USB_REQ_SET_SECURITY_DATA = 0x12;
+ private static final int USB_REQ_GET_SECURITY_DATA = 0x13;
+ private static final int USB_REQ_SET_WUSB_DATA = 0x14;
+ private static final int USB_REQ_LOOPBACK_DATA_WRITE = 0x15;
+ private static final int USB_REQ_LOOPBACK_DATA_READ = 0x16;
+ private static final int USB_REQ_SET_INTERFACE_DS = 0x17;
+
+ private static final int USB_REQ_STANDARD_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x10
+ private static final int USB_REQ_STANDARD_DEVICE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x90
+ private static final int USB_REQ_STANDARD_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x11
+ private static final int USB_REQ_STANDARD_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x91
+ private static final int USB_REQ_STANDARD_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x12
+ private static final int USB_REQ_STANDARD_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x92
+
+ private static final int USB_REQ_CS_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x20
+ private static final int USB_REQ_CS_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xa0
+ private static final int USB_REQ_CS_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x21
+ private static final int USB_REQ_CS_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xa1
+ private static final int USB_REQ_CS_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x22
+ private static final int USB_REQ_CS_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xa2
+
+ private static final int USB_REQ_VENDER_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x40
+ private static final int USB_REQ_VENDER_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xc0
+ private static final int USB_REQ_VENDER_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x41
+ private static final int USB_REQ_VENDER_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xc1
+ private static final int USB_REQ_VENDER_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x42
+ private static final int USB_REQ_VENDER_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xc2
+
+ private static final int USB_DT_DEVICE = 0x01;
+ private static final int USB_DT_CONFIG = 0x02;
+ private static final int USB_DT_STRING = 0x03;
+ private static final int USB_DT_INTERFACE = 0x04;
+ private static final int USB_DT_ENDPOINT = 0x05;
+ private static final int USB_DT_DEVICE_QUALIFIER = 0x06;
+ private static final int USB_DT_OTHER_SPEED_CONFIG = 0x07;
+ private static final int USB_DT_INTERFACE_POWER = 0x08;
+ private static final int USB_DT_OTG = 0x09;
+ private static final int USB_DT_DEBUG = 0x0a;
+ private static final int USB_DT_INTERFACE_ASSOCIATION = 0x0b;
+ private static final int USB_DT_SECURITY = 0x0c;
+ private static final int USB_DT_KEY = 0x0d;
+ private static final int USB_DT_ENCRYPTION_TYPE = 0x0e;
+ private static final int USB_DT_BOS = 0x0f;
+ private static final int USB_DT_DEVICE_CAPABILITY = 0x10;
+ private static final int USB_DT_WIRELESS_ENDPOINT_COMP = 0x11;
+ private static final int USB_DT_WIRE_ADAPTER = 0x21;
+ private static final int USB_DT_RPIPE = 0x22;
+ private static final int USB_DT_CS_RADIO_CONTROL = 0x23;
+ private static final int USB_DT_PIPE_USAGE = 0x24;
+ private static final int USB_DT_SS_ENDPOINT_COMP = 0x30;
+ private static final int USB_DT_CS_DEVICE = (USB_TYPE_CLASS | USB_DT_DEVICE);
+ private static final int USB_DT_CS_CONFIG = (USB_TYPE_CLASS | USB_DT_CONFIG);
+ private static final int USB_DT_CS_STRING = (USB_TYPE_CLASS | USB_DT_STRING);
+ private static final int USB_DT_CS_INTERFACE = (USB_TYPE_CLASS | USB_DT_INTERFACE);
+ private static final int USB_DT_CS_ENDPOINT = (USB_TYPE_CLASS | USB_DT_ENDPOINT);
+ private static final int USB_DT_DEVICE_SIZE = 18;
+
+ /**
+ * 指定したIDのStringディスクリプタから文字列を取得する。取得できなければnull
+ * @param connection
+ * @param id
+ * @param languageCount
+ * @param languages
+ * @return
+ */
+ private static String getString(final UsbDeviceConnection connection, final int id, final int languageCount, final byte[] languages) {
+ final byte[] work = new byte[256];
+ String result = null;
+ for (int i = 1; i <= languageCount; i++) {
+ int ret = connection.controlTransfer(
+ USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
+ USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | id, languages[i], work, 256, 0);
+ if ((ret > 2) && (work[0] == ret) && (work[1] == USB_DT_STRING)) {
+ // skip first two bytes(bLength & bDescriptorType), and copy the rest to the string
+ try {
+ result = new String(work, 2, ret - 2, "UTF-16LE");
+ if (!"Љ".equals(result)) { // 変なゴミが返ってくる時がある
+ break;
+ } else {
+ result = null;
+ }
+ } catch (final UnsupportedEncodingException e) {
+ // ignore
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * @param device
+ * @return
+ */
+ public UsbDeviceInfo getDeviceInfo(final UsbDevice device) {
+ return updateDeviceInfo(mUsbManager, device, null);
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * #updateDeviceInfo(final UsbManager, final UsbDevice, final UsbDeviceInfo)のヘルパーメソッド
+ * @param context
+ * @param device
+ * @return
+ */
+ public static UsbDeviceInfo getDeviceInfo(final Context context, final UsbDevice device) {
+ return updateDeviceInfo((UsbManager)context.getSystemService(Context.USB_SERVICE), device, new UsbDeviceInfo());
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * @param manager
+ * @param device
+ * @param _info
+ * @return
+ */
+ @TargetApi(Build.VERSION_CODES.M)
+ public static UsbDeviceInfo updateDeviceInfo(final UsbManager manager, final UsbDevice device, final UsbDeviceInfo _info) {
+ final UsbDeviceInfo info = _info != null ? _info : new UsbDeviceInfo();
+ info.clear();
+
+ if (device != null) {
+ if (BuildCheck.isLollipop()) {
+ info.manufacturer = device.getManufacturerName();
+ info.product = device.getProductName();
+ info.serial = device.getSerialNumber();
+ }
+ if (BuildCheck.isMarshmallow()) {
+ info.usb_version = device.getVersion();
+ }
+ if ((manager != null) && manager.hasPermission(device)) {
+ final UsbDeviceConnection connection = manager.openDevice(device);
+ if(connection == null) {
+ return null;
+ }
+ final byte[] desc = connection.getRawDescriptors();
+
+ if (TextUtils.isEmpty(info.usb_version)) {
+ info.usb_version = String.format("%x.%02x", ((int)desc[3] & 0xff), ((int)desc[2] & 0xff));
+ }
+ if (TextUtils.isEmpty(info.version)) {
+ info.version = String.format("%x.%02x", ((int)desc[13] & 0xff), ((int)desc[12] & 0xff));
+ }
+ if (TextUtils.isEmpty(info.serial)) {
+ info.serial = connection.getSerial();
+ }
+
+ final byte[] languages = new byte[256];
+ int languageCount = 0;
+ // controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
+ try {
+ int result = connection.controlTransfer(
+ USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
+ USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | 0, 0, languages, 256, 0);
+ if (result > 0) {
+ languageCount = (result - 2) / 2;
+ }
+ if (languageCount > 0) {
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = getString(connection, desc[14], languageCount, languages);
+ }
+ if (TextUtils.isEmpty(info.product)) {
+ info.product = getString(connection, desc[15], languageCount, languages);
+ }
+ if (TextUtils.isEmpty(info.serial)) {
+ info.serial = getString(connection, desc[16], languageCount, languages);
+ }
+ }
+ } finally {
+ connection.close();
+ }
+ }
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = USBVendorId.vendorName(device.getVendorId());
+ }
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = String.format("%04x", device.getVendorId());
+ }
+ if (TextUtils.isEmpty(info.product)) {
+ info.product = String.format("%04x", device.getProductId());
+ }
+ }
+ return info;
+ }
+
+ /**
+ * control class
+ * never reuse the instance when it closed
+ */
+ public static final class UsbControlBlock implements Cloneable {
+ private final WeakReference mWeakMonitor;
+ private final WeakReference mWeakDevice;
+ protected UsbDeviceConnection mConnection;
+ protected final UsbDeviceInfo mInfo;
+ private final int mBusNum;
+ private final int mDevNum;
+ private final SparseArray> mInterfaces = new SparseArray>();
+
+ /**
+ * this class needs permission to access USB device before constructing
+ * @param monitor
+ * @param device
+ */
+ private UsbControlBlock(final USBMonitor monitor, final UsbDevice device) {
+ if (DEBUG) XLogWrapper.i(TAG, "UsbControlBlock:constructor");
+ mWeakMonitor = new WeakReference(monitor);
+ mWeakDevice = new WeakReference(device);
+ mConnection = monitor.mUsbManager.openDevice(device);
+ mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
+ final String name = device.getDeviceName();
+ final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
+ int busnum = 0;
+ int devnum = 0;
+ if (v != null) {
+ busnum = Integer.parseInt(v[v.length-2]);
+ devnum = Integer.parseInt(v[v.length-1]);
+ }
+ mBusNum = busnum;
+ mDevNum = devnum;
+// if (DEBUG) {
+ if (mConnection != null) {
+ final int desc = mConnection.getFileDescriptor();
+ final byte[] rawDesc = mConnection.getRawDescriptors();
+ XLogWrapper.i(TAG, String.format(Locale.US, "name=%s,desc=%d,busnum=%d,devnum=%d,rawDesc=", name, desc, busnum, devnum) + rawDesc);
+ } else {
+ XLogWrapper.e(TAG, "could not connect to device " + name);
+ }
+// }
+ }
+
+ /**
+ * copy constructor
+ * @param src
+ * @throws IllegalStateException
+ */
+ private UsbControlBlock(final UsbControlBlock src) throws IllegalStateException {
+ final USBMonitor monitor = src.getUSBMonitor();
+ final UsbDevice device = src.getDevice();
+ if (device == null) {
+ throw new IllegalStateException("device may already be removed");
+ }
+ mConnection = monitor.mUsbManager.openDevice(device);
+ if (mConnection == null) {
+ throw new IllegalStateException("device may already be removed or have no permission");
+ }
+ mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
+ mWeakMonitor = new WeakReference(monitor);
+ mWeakDevice = new WeakReference(device);
+ mBusNum = src.mBusNum;
+ mDevNum = src.mDevNum;
+ // FIXME USBMonitor.mCtrlBlocksに追加する(今はHashMapなので追加すると置き換わってしまうのでだめ, ListかHashMapにListをぶら下げる?)
+ }
+
+ /**
+ * duplicate by clone
+ * need permission
+ * USBMonitor never handle cloned UsbControlBlock, you should release it after using it.
+ * @return
+ * @throws CloneNotSupportedException
+ */
+ @Override
+ public UsbControlBlock clone() throws CloneNotSupportedException {
+ final UsbControlBlock ctrlblock;
+ try {
+ ctrlblock = new UsbControlBlock(this);
+ } catch (final IllegalStateException e) {
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+ return ctrlblock;
+ }
+
+ public USBMonitor getUSBMonitor() {
+ return mWeakMonitor.get();
+ }
+
+ public final UsbDevice getDevice() {
+ return mWeakDevice.get();
+ }
+
+ /**
+ * get device name
+ * @return
+ */
+ public String getDeviceName() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getDeviceName() : "";
+ }
+
+ /**
+ * get device id
+ * @return
+ */
+ public int getDeviceId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getDeviceId() : 0;
+ }
+
+ /**
+ * get device key string
+ * @return same value if the devices has same vendor id, product id, device class, device subclass and device protocol
+ */
+ public String getDeviceKeyName() {
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get());
+ }
+
+ /**
+ * get device key string
+ * @param useNewAPI if true, try to use serial number
+ * @return
+ * @throws IllegalStateException
+ */
+ public String getDeviceKeyName(final boolean useNewAPI) throws IllegalStateException {
+ if (useNewAPI) checkConnection();
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, useNewAPI);
+ }
+
+ /**
+ * get device key
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceKey() throws IllegalStateException {
+ checkConnection();
+ return USBMonitor.getDeviceKey(mWeakDevice.get());
+ }
+
+ /**
+ * get device key
+ * @param useNewAPI if true, try to use serial number
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceKey(final boolean useNewAPI) throws IllegalStateException {
+ if (useNewAPI) checkConnection();
+ return USBMonitor.getDeviceKey(mWeakDevice.get(), mInfo.serial, useNewAPI);
+ }
+
+ /**
+ * get device key string
+ * if device has serial number, use it
+ * @return
+ */
+ public String getDeviceKeyNameWithSerial() {
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, false);
+ }
+
+ /**
+ * get device key
+ * if device has serial number, use it
+ * @return
+ */
+ public int getDeviceKeyWithSerial() {
+ return getDeviceKeyNameWithSerial().hashCode();
+ }
+
+ /**
+ * get UsbDeviceConnection
+ * @return
+ */
+ public synchronized UsbDeviceConnection getConnection() {
+ return mConnection;
+ }
+
+ /**
+ * get file descriptor to access USB device
+ * @return
+ * @throws IllegalStateException
+ */
+ public synchronized int getFileDescriptor() throws IllegalStateException {
+ checkConnection();
+ return mConnection.getFileDescriptor();
+ }
+
+ /**
+ * get raw descriptor for the USB device
+ * @return
+ * @throws IllegalStateException
+ */
+ public synchronized byte[] getRawDescriptors() throws IllegalStateException {
+ checkConnection();
+ return mConnection.getRawDescriptors();
+ }
+
+ /**
+ * get vendor id
+ * @return
+ */
+ public int getVenderId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getVendorId() : 0;
+ }
+
+ /**
+ * get product id
+ * @return
+ */
+ public int getProductId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getProductId() : 0;
+ }
+
+ /**
+ * get version string of USB
+ * @return
+ */
+ public String getUsbVersion() {
+ return mInfo.usb_version;
+ }
+
+ /**
+ * get manufacture
+ * @return
+ */
+ public String getManufacture() {
+ return mInfo.manufacturer;
+ }
+
+ /**
+ * get product name
+ * @return
+ */
+ public String getProductName() {
+ return mInfo.product;
+ }
+
+ /**
+ * get version
+ * @return
+ */
+ public String getVersion() {
+ return mInfo.version;
+ }
+
+ /**
+ * get serial number
+ * @return
+ */
+ public String getSerial() {
+ return mInfo.serial;
+ }
+
+ public int getBusNum() {
+ return mBusNum;
+ }
+
+ public int getDevNum() {
+ return mDevNum;
+ }
+
+ /**
+ * get interface
+ * @param interface_id
+ * @throws IllegalStateException
+ */
+ public synchronized UsbInterface getInterface(final int interface_id) throws IllegalStateException {
+ return getInterface(interface_id, 0);
+ }
+
+ /**
+ * get interface
+ * @param interface_id
+ * @param altsetting
+ * @return
+ * @throws IllegalStateException
+ */
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public synchronized UsbInterface getInterface(final int interface_id, final int altsetting) throws IllegalStateException {
+ checkConnection();
+ SparseArray intfs = mInterfaces.get(interface_id);
+ if (intfs == null) {
+ intfs = new SparseArray();
+ mInterfaces.put(interface_id, intfs);
+ }
+ UsbInterface intf = intfs.get(altsetting);
+ if (intf == null) {
+ final UsbDevice device = mWeakDevice.get();
+ final int n = device.getInterfaceCount();
+ for (int i = 0; i < n; i++) {
+ final UsbInterface temp = device.getInterface(i);
+ if ((temp.getId() == interface_id) && (temp.getAlternateSetting() == altsetting)) {
+ intf = temp;
+ break;
+ }
+ }
+ if (intf != null) {
+ intfs.append(altsetting, intf);
+ }
+ }
+ return intf;
+ }
+
+ /**
+ * open specific interface
+ * @param intf
+ */
+ public synchronized void claimInterface(final UsbInterface intf) {
+ claimInterface(intf, true);
+ }
+
+ public synchronized void claimInterface(final UsbInterface intf, final boolean force) {
+ checkConnection();
+ mConnection.claimInterface(intf, force);
+ }
+
+ /**
+ * close interface
+ * @param intf
+ * @throws IllegalStateException
+ */
+ public synchronized void releaseInterface(final UsbInterface intf) throws IllegalStateException {
+ checkConnection();
+ final SparseArray intfs = mInterfaces.get(intf.getId());
+ if (intfs != null) {
+ final int index = intfs.indexOfValue(intf);
+ intfs.removeAt(index);
+ if (intfs.size() == 0) {
+ mInterfaces.remove(intf.getId());
+ }
+ }
+ mConnection.releaseInterface(intf);
+ }
+
+ /**
+ * Close device
+ * This also close interfaces if they are opened in Java side
+ */
+ public synchronized void close() {
+ if (DEBUG) XLogWrapper.i(TAG, "UsbControlBlock#close:");
+
+ if (mConnection != null) {
+ final int n = mInterfaces.size();
+ for (int i = 0; i < n; i++) {
+ final SparseArray intfs = mInterfaces.valueAt(i);
+ if (intfs != null) {
+ final int m = intfs.size();
+ for (int j = 0; j < m; j++) {
+ final UsbInterface intf = intfs.valueAt(j);
+ mConnection.releaseInterface(intf);
+ }
+ intfs.clear();
+ }
+ }
+ mInterfaces.clear();
+ mConnection.close();
+ mConnection = null;
+ final USBMonitor monitor = mWeakMonitor.get();
+ if (monitor != null) {
+ if (monitor.mOnDeviceConnectListener != null) {
+ monitor.mOnDeviceConnectListener.onDisconnect(mWeakDevice.get(), UsbControlBlock.this);
+ }
+ monitor.mCtrlBlocks.remove(getDevice());
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == null) return false;
+ if (o instanceof UsbControlBlock) {
+ final UsbDevice device = ((UsbControlBlock) o).getDevice();
+ return device == null ? mWeakDevice.get() == null
+ : device.equals(mWeakDevice.get());
+ } else if (o instanceof UsbDevice) {
+ return o.equals(mWeakDevice.get());
+ }
+ return super.equals(o);
+ }
+
+// @Override
+// protected void finalize() throws Throwable {
+/// close();
+// super.finalize();
+// }
+
+ private synchronized void checkConnection() throws IllegalStateException {
+ if (mConnection == null) {
+ throw new IllegalStateException("already closed");
+ }
+ }
+ }
+
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/USBVendorId.java b/libuvc/src/main/java/com/serenegiant/usb/USBVendorId.java
new file mode 100644
index 0000000000000000000000000000000000000000..d354b66ca701049fe38a5b049c9185f1eb2cde67
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/USBVendorId.java
@@ -0,0 +1,859 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.util.SparseArray;
+
+public class USBVendorId {
+ private static final SparseArray IDS = new SparseArray();
+
+ public static String vendorName(final int vendor_id) {
+ return IDS.get(vendor_id);
+ }
+
+ static {
+ IDS.put(10006, "YUEN DA ELECTRONIC PRODUCTS FACTORY");
+ IDS.put(10013, "Gionee Communication Equipment Co., Ltd. ShenZhen");
+ IDS.put(10022, "Universal Electronics Inc. (dba: TVIEW)");
+ IDS.put(1003, "Atmel Corporation");
+ IDS.put(1006, "Mitsumi");
+ IDS.put(1008, "HP Inc.");
+ IDS.put(10112, "M31 Technology Corp.");
+ IDS.put(10113, "Liteconn Co., Ltd.");
+ IDS.put(10121, "Suzhou WEIJU Electronics Technology Co., Ltd.");
+ IDS.put(10144, "Mondokey Limited");
+ IDS.put(10149, "Advantest Corporation");
+ IDS.put(10150, "iRobot Corporation");
+ IDS.put(1020, "Elitegroup Computer Systems");
+ IDS.put(1021, "Xilinx Inc.");
+ IDS.put(10226, "Sibridge Tech.");
+ IDS.put(1026, "ALi Corporation");
+ IDS.put(1027, "Future Technology Devices International Limited");
+ IDS.put(10275, "Dongguan Jiumutong Industry Co., Ltd.");
+ IDS.put(10289, "Power Integrations");
+ IDS.put(10291, "Oculus VR, Inc.");
+ IDS.put(10300, "HIGH TEK HARNESS ENTERPRISE CO., LTD.");
+ IDS.put(10316, "Full in Hope Co., Ltd.");
+ IDS.put(1032, "Quanta Computer Inc.");
+ IDS.put(10329, "Viconn Technology (HK) Co., Ltd.");
+ IDS.put(1033, "NEC Corporation");
+ IDS.put(1035, "Weltrend Semiconductor");
+ IDS.put(1037, "VIA Technologies, Inc.");
+ IDS.put(10374, "Seeed Technology Co., Ltd.");
+ IDS.put(10375, "Specwerkz");
+ IDS.put(1038, "MCCI Corporation");
+ IDS.put(10398, "Esselte Leitz GmbH & Co. KG");
+ IDS.put(10406, "E-SEEK Inc.");
+ IDS.put(1041, "BUFFALO INC.");
+ IDS.put(10423, "Pleora Technologies Inc.");
+ IDS.put(10431, "Vitetech Int'l Co., Ltd.");
+ IDS.put(1044, "Giga-Byte Technology Co., Ltd.");
+ IDS.put(10446, "Changzhou Shi Wujin Miqi East Electronic Co., Ltd.");
+ IDS.put(10457, "Shenzhen Ourconn Technology Co., Ltd.");
+ IDS.put(10458, "G.SKILL Int'l Enterprice Co., Ltd.");
+ IDS.put(1046, "Nuvoton Technology Corp.");
+ IDS.put(10466, "Surplus Electronic Technology Co., Ltd.");
+ IDS.put(10470, "BIAMP SYSTEMS");
+ IDS.put(10509, "IBCONN Technologies (Shenzhen) Co., Ltd.");
+ IDS.put(10510, "Fugoo Inc.");
+ IDS.put(10519, "Pan Xin Precision Electronics Co., Ltd.");
+ IDS.put(10530, "Dongguan Digi-in Digital Technology Co., Ltd.");
+ IDS.put(1054, "Creative Labs");
+ IDS.put(10540, "GENUSION, Inc.");
+ IDS.put(10544, "Ineda Systems Inc.");
+ IDS.put(10545, "Jolla Ltd.");
+ IDS.put(10546, "Peraso Technologies, Inc.");
+ IDS.put(10549, "Nanjing Magewell Electronics Co., Ltd.");
+ IDS.put(10560, "Shenzhen Yiwanda Electronics Co., Ltd.");
+ IDS.put(1057, "Nokia Corporation");
+ IDS.put(10575, "Dollar Connection Ltd.");
+ IDS.put(10595, "BIO-key International, Inc.");
+ IDS.put(1060, "Microchip-SMSC");
+ IDS.put(10603, "Xacti Corporation");
+ IDS.put(10615, "Shenzhen Zowee Technology Co., Ltd.");
+ IDS.put(10643, "ADPlaus Technology Limited");
+ IDS.put(10646, "Unwired Technology");
+ IDS.put(1065, "Cirrus Logic Inc.");
+ IDS.put(10657, "Union Electric Plug & Connector Corp.");
+ IDS.put(10674, "Canova Tech");
+ IDS.put(10685, "Silicon Works");
+ IDS.put(10695, "HANRICO ANFU ELECTRONICS CO., LTD.");
+ IDS.put(10700, "Kodak Alaris");
+ IDS.put(10702, "JGR Optics Inc.");
+ IDS.put(10703, "Richtek Technology Corporation");
+ IDS.put(10705, "Binatone Electronics Int. Ltd.");
+ IDS.put(1071, "Molex Inc.");
+ IDS.put(10715, "Shenzhen iBoard Technology Co., Ltd.");
+ IDS.put(10719, "SMIT(HK) Limited");
+ IDS.put(1072, "Fujitsu Component Limited");
+ IDS.put(10725, "Dongguan Kechenda Electronic Technology Co., Ltd.");
+ IDS.put(10726, "Fengshun Peiying Electro-Acoustic Co., Ltd.");
+ IDS.put(10744, "MD ELEKTRONIK GmbH");
+ IDS.put(10749, "Bad Elf, LLC");
+ IDS.put(10770, "Vreo Limited");
+ IDS.put(10772, "Kanex");
+ IDS.put(10781, "Oxford Nanopore Technologies");
+ IDS.put(10782, "Obsidian Technology");
+ IDS.put(10783, "Lucent Trans Electronics Co., Ltd.");
+ IDS.put(10784, "GUOGUANG GROUP CO., LTD.");
+ IDS.put(10788, "CNPLUS");
+ IDS.put(10789, "Fourstar Group");
+ IDS.put(10790, "Tragant International Co., Ltd.");
+ IDS.put(10791, "DongGuan LianGang Optoelectronic Technology Co., Ltd.");
+ IDS.put(10797, "Atrust Computer Corp.");
+ IDS.put(10798, "VIA Alliance Semiconductor Co., Ltd.");
+ IDS.put(10799, "BSUN Electronics Co., Ltd.");
+ IDS.put(1080, "Advanced Micro Devices");
+ IDS.put(10807, "RTD Embedded Technologies, Inc.");
+ IDS.put(10816, "Shenzhen Choseal Industrial Co., Ltd.");
+ IDS.put(10817, "Canyon Semiconductor");
+ IDS.put(10818, "Spectra7 Microsystems Corp.");
+ IDS.put(10821, "Meizu Technology Co., Ltd.");
+ IDS.put(10822, "Hubei Yingtong Telecommunication Cable Inc.");
+ IDS.put(10829, "Wilder Technologies");
+ IDS.put(10837, "Diodes Inc.");
+ IDS.put(10846, "DuPont");
+ IDS.put(1085, "Lexmark International Inc.");
+ IDS.put(10852, "Zhejiang Songcheng Electronics Co., Ltd.");
+ IDS.put(10859, "VSN Mobil");
+ IDS.put(10875, "Bellwether Electronic Corp.");
+ IDS.put(10878, "VAIO Corporation");
+ IDS.put(10879, "Perixx Computer GmbH");
+ IDS.put(10885, "HANK ELECTRONICS CO., LTD");
+ IDS.put(10892, "Sonnet Technologies, Inc.");
+ IDS.put(10893, "Keysight Technologies Inc.");
+ IDS.put(10895, "Manutronics Vietnam Joint Stock Company");
+ IDS.put(10900, "G2 Touch Co., Ltd.");
+ IDS.put(10902, "Micromax Informatics Ltd");
+ IDS.put(10910, "SEIKO SOLUTIONS Inc.");
+ IDS.put(10912, "Casco Products Corp.");
+ IDS.put(10922, "Virtium Technology, Inc.");
+ IDS.put(10923, "Field and Company LLC, dba Leef USA");
+ IDS.put(10928, "GM Global Technology Operations LLC");
+ IDS.put(10931, "Key Asic Inc.");
+ IDS.put(10943, "Revolabs, Inc.");
+ IDS.put(10945, "Lattice Semiconductor Corp");
+ IDS.put(10947, "Foshan Nanhai Saga Audio Equipment Co., Ltd.");
+ IDS.put(10957, "Silergy Corp.");
+ IDS.put(10963, "Shenzhen Hali-Power Industrial Co., Ltd.");
+ IDS.put(10971, "I-PEX (Dai-ichi Seiko)");
+ IDS.put(10973, "SEE-PLUS INDUSTRIAL LTD.");
+ IDS.put(10990, "Adapt-IP Company");
+ IDS.put(10997, "Libratone A/S");
+ IDS.put(10999, "Shenzhen Hazens Automotive Electronics (SZ) Co., Ltd.");
+ IDS.put(11000, "Jiangsu Toppower Automotive Electronics Co., Ltd.");
+ IDS.put(11001, "Drapho Electronics Technology Co., Ltd.");
+ IDS.put(1102, "Alps Electric Co., Ltd.");
+ IDS.put(11022, "Le Shi Zhi Xin Electronic Technology (Tian Jin) Limited");
+ IDS.put(11024, "Cardiac Insight, Inc.");
+ IDS.put(11028, "EverPro Technologies Company, Ltd.");
+ IDS.put(11029, "Rosenberger Hochfrequenztechnik");
+ IDS.put(11035, "Dongguan City Sanji Electronics Co., Ltd.");
+ IDS.put(11037, "Lintes Technology Co., Ltd.");
+ IDS.put(11039, "KinnexA, Inc.");
+ IDS.put(11042, "Metra Electronics Corp.");
+ IDS.put(11044, "KeepKey, LLC");
+ IDS.put(11047, "FluxData Incorporated");
+ IDS.put(1105, "Texas Instruments");
+ IDS.put(11061, "Assem Technology Co., Ltd.");
+ IDS.put(11062, "Dongguan City Jianghan Electronics Co., Ltd.");
+ IDS.put(11063, "Huizhou Desay SV Automotive Co., Ltd.");
+ IDS.put(11064, "Ningbo Rixing Electronics Co., Ltd.");
+ IDS.put(11069, "GuangDong YuanFeng Automotive Electroics Co., Ltd.");
+ IDS.put(11080, "Sounding Audio Industrial Limited");
+ IDS.put(11082, "Yueqing Huaxin Electronic Co., Ltd.");
+ IDS.put(11098, "Universal Audio, Inc.");
+ IDS.put(11111, "Lifesize, Inc.");
+ IDS.put(11123, "Pioneer DJ Corporation");
+ IDS.put(11124, "Embedded Intelligence, Inc.");
+ IDS.put(11125, "New Matter");
+ IDS.put(11126, "Shanghai Wingtech Electronic Technology Co., Ltd.");
+ IDS.put(11127, "Epiphan Systems Inc.");
+ IDS.put(11130, "Spin Master Far East Ltd.");
+ IDS.put(11131, "Gigaset Digital Technology (Shenzhen) Co., Ltd.");
+ IDS.put(11132, "Noveltek Semiconductor Corp.");
+ IDS.put(11139, "Silicon Line GmbH");
+ IDS.put(11140, "Ever Win International Corp.");
+ IDS.put(11144, "Socionext Inc.");
+ IDS.put(11145, "Ugreen Group Limited");
+ IDS.put(11146, "Shanghai Pateo Electronic Equipment Mfg. Co., Ltd.");
+ IDS.put(1115, "Renesas Electronics Corp.");
+ IDS.put(11154, "i-BLADES, Inc.");
+ IDS.put(11155, "Altia Systems Inc.");
+ IDS.put(11156, "ShenZhen Baoyuanda Electronics Co., Ltd.");
+ IDS.put(11157, "iST - Integrated Service Technology Inc.");
+ IDS.put(11158, "HYUNDAI MOBIS Co., Ltd.");
+ IDS.put(11161, "360fly, Inc.");
+ IDS.put(11162, "HUIZHOU CHENG SHUO HARDWARE PLASTIC CO., LTD.");
+ IDS.put(11163, "Zhongshan Aute Electronics Technology Co., Ltd.");
+ IDS.put(11164, "Guangdong King Link Industrial Co., Ltd.");
+ IDS.put(11167, "Scietera Technologies, Inc.");
+ IDS.put(11168, "InVue Security Products");
+ IDS.put(11169, "I-Sheng Electric Wire & Cable Co., Ltd.");
+ IDS.put(11170, "China Daheng Group Inc Beijing Image Vision Tech Branch");
+ IDS.put(11171, "Shenzhen FeiTianXia Technology Ltd.");
+ IDS.put(11172, "Shenzhen HengJia New Energy Auto Part Co., Ltd.");
+ IDS.put(11175, "77 Elektronika Kft.");
+ IDS.put(11176, "YUDU EASON ELECTRONIC CO., LTD.");
+ IDS.put(1118, "Microsoft Corporation");
+ IDS.put(11181, "XIN JI (SHENZHEN) COMPUTER PARTS CO., LTD.");
+ IDS.put(11189, "Silk ID Systems");
+ IDS.put(11190, "3D Imaging & Simulations Corp. (3DISC)");
+ IDS.put(11191, "Dongguan ChengXiang Industrial Co., Ltd.");
+ IDS.put(11192, "OCC (Zhuhai) Electronic Co., Ltd.");
+ IDS.put(11194, "Sinseader Electronic Co., Ltd.");
+ IDS.put(11195, "DONGGUAN YELLOWKNIFE Industrial Co., Ltd.");
+ IDS.put(11197, "RF Creations Ltd.");
+ IDS.put(11198, "Chengyi Semiconductors (Shanghai) Co., Ltd.");
+ IDS.put(11199, "Shenzhen Shinning Electronic Co., Ltd.");
+ IDS.put(11200, "Shenzhen WFD Electronics Co., Ltd.");
+ IDS.put(11201, "Dongguan Sino Syncs Industrial Co., Ltd.");
+ IDS.put(11202, "JNTC Co., Ltd.");
+ IDS.put(11208, "DONGGUAN POLIXIN ELECTRIC CO., LTD.");
+ IDS.put(11209, "Tama Electric (Suzhou) Co., Ltd.");
+ IDS.put(1121, "Primax Electronics");
+ IDS.put(11210, "Exvision, Inc.");
+ IDS.put(11216, "mophie, LLC");
+ IDS.put(11219, "Dongguan ULT-unite electronic technology co., LTD");
+ IDS.put(11220, "JL Audio, Inc.");
+ IDS.put(11221, "Cable Matters Inc.");
+ IDS.put(11222, "CoroWare, Inc.");
+ IDS.put(11229, "Charm Sciences Inc.");
+ IDS.put(1123, "EATON");
+ IDS.put(11230, "Pickering Interfaces Limited");
+ IDS.put(11231, "Hangzhou Hikvision Digital Technology Co., Ltd.");
+ IDS.put(11232, "FULLINK ELECTRONICS TECHNOLOGY (SZ) LTD");
+ IDS.put(11233, "AutoChips Inc.");
+ IDS.put(11234, "Electric Connector Technology Co., Ltd.");
+ IDS.put(11237, "LELTEK");
+ IDS.put(11238, "Dongguan KaiWin Electronics Co., Ltd.");
+ IDS.put(11239, "BEFS Co., Ltd.");
+ IDS.put(11240, "Archisite, Inc.");
+ IDS.put(11241, "Magneti Marelli S.p.A Electr BL");
+ IDS.put(11246, "Ventev Mobile");
+ IDS.put(11247, "Quanta Storage Inc.");
+ IDS.put(11248, "Tech-Top Technology Limited");
+ IDS.put(11253, "Shenzhen YOOBAO Technology Co., Ltd.");
+ IDS.put(11254, "Shenzhen Sinotek Technology Co., Ltd.");
+ IDS.put(11255, "KEYW");
+ IDS.put(11256, "Visual Land Inc.");
+ IDS.put(11264, "MEEM SL Ltd");
+ IDS.put(11265, "Dongguan Arin Electronics Technology Co., Ltd.");
+ IDS.put(11266, "DongGuan City JianNuo Electronics Co., Ltd.");
+ IDS.put(11268, "Shenzhen XOX Electronics Co., Ltd.");
+ IDS.put(11269, "Protop International Inc.");
+ IDS.put(11270, "Microsemi Semiconductor (US) Inc.");
+ IDS.put(11271, "Webcloak LLC");
+ IDS.put(11272, "INVECAS INC.");
+ IDS.put(11274, "ATANS Technology Inc.");
+ IDS.put(11275, "Triple Win Precision Technology Co., Ltd.");
+ IDS.put(11276, "IC Realtech");
+ IDS.put(11277, "Embrava Pty Ltd");
+ IDS.put(1128, "Wieson Technologies Co., Ltd.");
+ IDS.put(11280, "Sinotronics Co., Ltd.");
+ IDS.put(11281, "ALLBEST ELECTRONICS TECHNOLOGY CO., LTD.");
+ IDS.put(11282, "Shenzhen Xin Kai Feng Electronics Factory");
+ IDS.put(11283, "MOST WELL Technology Corp.");
+ IDS.put(11284, "Buffalo Memory Co., Ltd.");
+ IDS.put(11285, "Xentris Wireless");
+ IDS.put(11286, "Priferential Accessories Ltd");
+ IDS.put(11289, "Sunlike Technology Co., Ltd.");
+ IDS.put(11290, "Young Fast Optoelectronics Co., Ltd.");
+ IDS.put(11291, "ISAW Camera Inc");
+ IDS.put(11298, "Qanba USA, LLC");
+ IDS.put(11299, "Super Micro Computer Inc.");
+ IDS.put(11302, "Micromax International Corporation");
+ IDS.put(11304, "Granite River Labs Japan Ltd.");
+ IDS.put(11305, "Coagent Enterprise Limited");
+ IDS.put(11306, "LEIA Inc.");
+ IDS.put(11309, "Shenzhen Ebull Technology Limited");
+ IDS.put(1131, "American Megatrends");
+ IDS.put(11310, "Hualun Technology Co., Ltd.");
+ IDS.put(11311, "Sensel, Inc.");
+ IDS.put(11319, "Shenzhen Adition Audio Science & Technology Co., Ltd.");
+ IDS.put(11320, "Goldenconn Electronics Technology (Suzhou) Co., Ltd.");
+ IDS.put(11321, "JIB Electronics Technology Co., Ltd.");
+ IDS.put(11322, "Changzhou Shinco Automotive Electronics Co., Ltd.");
+ IDS.put(11323, "Shenzhen Hangsheng Electronics Corp., Ltd.");
+ IDS.put(11324, "Beartooth Radio, Inc.");
+ IDS.put(11325, "Audience, A Knowles Company");
+ IDS.put(11327, "Nextbit Systems, Inc.");
+ IDS.put(11328, "Leadtrend");
+ IDS.put(11329, "Adaptertek Technology Co., Ltd.");
+ IDS.put(1133, "Logitech Inc.");
+ IDS.put(11330, "Feature Integration Technology Inc.");
+ IDS.put(11331, "Avegant Corporation");
+ IDS.put(11335, "Chunghsin International Electronics Co., Ltd.");
+ IDS.put(11336, "Delphi Electrical Centers (Shanghai) Co., Ltd.");
+ IDS.put(11341, "VVETEK DOO");
+ IDS.put(11347, "Huizhou Foryou General Electronics Co., Ltd.");
+ IDS.put(11348, "LifeWatch Technologies Ltd.");
+ IDS.put(11349, "Magicleap");
+ IDS.put(11355, "Dongguan City Shenglan Electronics Co., LTD.");
+ IDS.put(11356, "Neusoft Corporation");
+ IDS.put(11357, "SIP Simya Electronics Technology Co., Ltd.");
+ IDS.put(11358, "GNSD Automotive Co., Ltd.");
+ IDS.put(11359, "YOODS Co., Ltd.");
+ IDS.put(11360, "Sirin Mobile Technologies AG");
+ IDS.put(11361, "Jadmam Corporation dba: Boytone");
+ IDS.put(11373, "Gibson Innovations");
+ IDS.put(11374, "Shen Zhen Xian Shuo Technology Co. LTD");
+ IDS.put(11375, "PST Eletronica LTDA");
+ IDS.put(11376, "PERI, Inc.");
+ IDS.put(11377, "Bozhou BoTong Information Technology Co., Ltd.");
+ IDS.put(11383, "Profindustry GmbH");
+ IDS.put(11384, "BRAGI GmbH");
+ IDS.put(11385, "WAWGD, Inc. (DBA: Foresight Sports)");
+ IDS.put(11390, "Dongguan Allpass Electronic Co., Ltd.");
+ IDS.put(11391, "SHENZHEN D-VITEC INDUSTRIAL CO., LTD.");
+ IDS.put(11392, "motomobile AG");
+ IDS.put(11393, "Indie Semiconductor");
+ IDS.put(11397, "Audientes");
+ IDS.put(11403, "Huizhou Dehong Technology Co., Ltd.");
+ IDS.put(11404, "PowerCenter Technology Limited");
+ IDS.put(11405, "Mizco International, Inc.");
+ IDS.put(11408, "I. AM. PLUS, LLC");
+ IDS.put(11409, "Corigine, Inc.");
+ IDS.put(11410, "Ningbo Yinzhou Shengke Electronics Co., Ltd.");
+ IDS.put(11417, "Prusa Research s.r.o.");
+ IDS.put(11423, "e-Smart Systems Pvt. Ltd.");
+ IDS.put(11424, "Leagtech Jiangxi Electronic Co., Ltd.");
+ IDS.put(11425, "Dongguan Yujia Electronics Technology Co., Ltd.");
+ IDS.put(11426, "GuangZhou MingPing Electronics Technology");
+ IDS.put(11427, "DJI Technology Co., Ltd.");
+ IDS.put(11428, "Shenzhen Alex Technology Co., Ltd.");
+ IDS.put(11433, "JITS TECHNOLOGY CO., LIMITED");
+ IDS.put(11434, "LIVV Brand llc");
+ IDS.put(11444, "Ava Enterprises, Inc. dba: Boss Audio Systems");
+ IDS.put(11448, "Shenzhen Sydixon Electronic Technology Co., Ltd.");
+ IDS.put(11449, "On-Bright Electronics (Shanghai) Co., Ltd.");
+ IDS.put(11450, "Dongguan Puxu Industrial Co., Ltd.");
+ IDS.put(11451, "Shenzhen Soling Indusrtial Co., Ltd.");
+ IDS.put(11453, "EGGCYTE, INC.");
+ IDS.put(11455, "Donggguan Yuhua Electronic Co., Ltd.");
+ IDS.put(11456, "Hangzhou Zero Zero Technology Co., Ltd.");
+ IDS.put(11462, "Prodigy Technovations Pvt Ltd");
+ IDS.put(11463, "EmergiTech, Inc");
+ IDS.put(11464, "Hewlett Packard Enterprise");
+ IDS.put(11465, "Monolithic Power Systems Inc.");
+ IDS.put(11467, "USB Memory Direct");
+ IDS.put(11468, "Silicon Mitus Inc.");
+ IDS.put(11472, "Technics Global Electronics & JCE Co., Ltd.");
+ IDS.put(11478, "Immersive Media");
+ IDS.put(11479, "Cosemi Technologies Inc.");
+ IDS.put(11481, "Cambrionix Ltd");
+ IDS.put(11482, "CXUN Co. Ltd.");
+ IDS.put(11483, "China Tsp Inc");
+ IDS.put(11490, "Yanfeng Visteon (Chongqing) Automotive Electronics Co");
+ IDS.put(11491, "Alcorlink Corp.");
+ IDS.put(11492, "ISBC Ltd.");
+ IDS.put(11493, "InX8 Inc dba: AKiTiO");
+ IDS.put(11494, "SDAN Tecchnology Co., Ltd.");
+ IDS.put(11495, "Lemobile Information Technology (Beijing) Co., Ltd.");
+ IDS.put(11496, "GongGuan HWX Electronic Technology Co., Ltd.");
+ IDS.put(11497, "Suzhu Jingshi Electronic Technology Co., Ltd.");
+ IDS.put(11498, "Zhong Shan City Richsound Electronic Industrial Ltd.");
+ IDS.put(11499, "Dongguang Kangbang Electronics Co., Ltd.");
+ IDS.put(1151, "Plantronics, Inc.");
+ IDS.put(1154, "Kyocera Corporation");
+ IDS.put(1155, "STMicroelectronics");
+ IDS.put(1161, "Foxconn / Hon Hai");
+ IDS.put(1165, "ITE Tech Inc.");
+ IDS.put(1177, "Yamaha Corporation");
+ IDS.put(1188, "Hitachi, Ltd.");
+ IDS.put(1191, "Visioneer");
+ IDS.put(1193, "Canon Inc.");
+ IDS.put(1200, "Nikon Corporation");
+ IDS.put(1201, "Pan International");
+ IDS.put(1204, "Cypress Semiconductor");
+ IDS.put(1205, "ROHM Co., Ltd.");
+ IDS.put(1207, "Compal Electronics, Inc.");
+ IDS.put(1208, "Seiko Epson Corp.");
+ IDS.put(1211, "I-O Data Device, Inc.");
+ IDS.put(1221, "Fujitsu Ltd.");
+ IDS.put(1227, "FUJIFILM Corporation");
+ IDS.put(1238, "Mentor Graphics");
+ IDS.put(1240, "Microchip Technology Inc.");
+ IDS.put(1241, "Holtek Semiconductor, Inc.");
+ IDS.put(1242, "Panasonic Corporation");
+ IDS.put(1245, "Sharp Corporation");
+ IDS.put(1250, "Exar Corporation");
+ IDS.put(1254, "Identiv, Inc.");
+ IDS.put(1256, "Samsung Electronics Co., Ltd.");
+ IDS.put(1260, "Tokyo Electron Device Limited");
+ IDS.put(1266, "Chicony Electronics Co., Ltd.");
+ IDS.put(1271, "Newnex Technology Corp.");
+ IDS.put(1273, "Brother Industries, Ltd.");
+ IDS.put(1276, "SUNPLUS TECHNOLOGY CO., LTD.");
+ IDS.put(1278, "PFU Limited");
+ IDS.put(1281, "Fujikura/DDK");
+ IDS.put(1282, "Acer, Inc.");
+ IDS.put(1287, "Hosiden Corporation");
+ IDS.put(1293, "Belkin International, Inc.");
+ IDS.put(1300, "FCI Electronics");
+ IDS.put(1302, "Longwell Electronics/Longwell Company");
+ IDS.put(1305, "Star Micronics Co., LTD");
+ IDS.put(1309, "American Power Conversion");
+ IDS.put(1314, "ACON, Advanced-Connectek, Inc.");
+ IDS.put(1343, "Synopsys, Inc.");
+ IDS.put(1356, "Sony Corporation");
+ IDS.put(1360, "Fuji Xerox Co., Ltd.");
+ IDS.put(1367, "ATEN International Co. Ltd.");
+ IDS.put(1369, "Cadence Design Systems, Inc.");
+ IDS.put(1386, "WACOM Co., Ltd.");
+ IDS.put(1389, "EIZO Corporation");
+ IDS.put(1390, "Elecom Co., Ltd.");
+ IDS.put(1394, "Conexant Systems, Inc.");
+ IDS.put(1398, "BAFO/Quality Computer Accessories");
+ IDS.put(1403, "Y-E Data, Inc.");
+ IDS.put(1404, "AVM GmbH");
+ IDS.put(1410, "Roland Corporation");
+ IDS.put(1412, "RATOC Systems, Inc.");
+ IDS.put(1419, "Infineon Technologies");
+ IDS.put(1423, "Alcor Micro, Corp.");
+ IDS.put(1424, "OMRON Corporation");
+ IDS.put(1447, "Bose Corporation");
+ IDS.put(1449, "OmniVision Technologies, Inc.");
+ IDS.put(1452, "Apple");
+ IDS.put(1453, "Y.C. Cable U.S.A., Inc");
+ IDS.put(14627, "National Instruments");
+ IDS.put(1470, "Tyco Electronics Corp., a TE Connectivity Ltd. company");
+ IDS.put(1473, "MegaChips Corporation");
+ IDS.put(1478, "Qualcomm, Inc");
+ IDS.put(1480, "Foxlink/Cheng Uei Precision Industry Co., Ltd.");
+ IDS.put(1482, "Ricoh Company Ltd.");
+ IDS.put(1498, "Microtek International Inc.");
+ IDS.put(1504, "Symbol Technologies");
+ IDS.put(1507, "Genesys Logic, Inc.");
+ IDS.put(1509, "Fuji Electric Co., Ltd.");
+ IDS.put(1525, "Unixtar Technology Inc.");
+ IDS.put(1529, "Datalogic ADC");
+ IDS.put(1535, "LeCroy Corporation");
+ IDS.put(1539, "Novatek Microelectronics Corp.");
+ IDS.put(1545, "SMK Manufacturing Inc.");
+ IDS.put(1551, "Joinsoon Electronics Mfg. Co., Ltd.");
+ IDS.put(1555, "TransAct Technologies Incorporated");
+ IDS.put(1561, "Seiko Instruments Inc.");
+ IDS.put(1582, "JPC/MAIN SUPER Inc.");
+ IDS.put(1583, "Sin Sheng Terminal & Machine Inc.");
+ IDS.put(1593, "Chrontel, Inc.");
+ IDS.put(1611, "Analog Devices, Inc. Development Tools");
+ IDS.put(1612, "Ji-Haw Industrial Co., Ltd");
+ IDS.put(1614, "Suyin Corporation");
+ IDS.put(1621, "Space Shuttle Hi-Tech Co.,Ltd.");
+ IDS.put(1622, "Glory Mark Electronic Ltd.");
+ IDS.put(1623, "Tekcon Electronics Corp.");
+ IDS.put(1624, "Sigma Designs, Inc.");
+ IDS.put(1631, "Good Way Technology Co., Ltd. & GWC technology Inc");
+ IDS.put(1632, "TSAY-E (BVI) International Inc.");
+ IDS.put(1633, "Hamamatsu Photonics K.K.");
+ IDS.put(1642, "Total Technologies, Ltd.");
+ IDS.put(1659, "Prolific Technology, Inc.");
+ IDS.put(16700, "Dell Inc.");
+ IDS.put(1680, "Golden Bridge Electech Inc.");
+ IDS.put(1689, "Tektronix, Inc.");
+ IDS.put(1690, "Askey Computer Corporation");
+ IDS.put(1709, "Greatland Electronics Taiwan Ltd.");
+ IDS.put(1710, "Eurofins Digital Testing Belgium");
+ IDS.put(1720, "Pixela Corporation");
+ IDS.put(1724, "Oki Data Corporation");
+ IDS.put(1727, "Leoco Corporation");
+ IDS.put(1732, "Bizlink Technology, Inc.");
+ IDS.put(1736, "SIIG, Inc.");
+ IDS.put(1747, "Mitsubishi Electric Corporation");
+ IDS.put(1758, "Heisei Technology Co., Ltd.");
+ IDS.put(1802, "Oki Electric Industry Co., Ltd.");
+ IDS.put(1805, "Comoss Electronic Co., Ltd.");
+ IDS.put(1809, "Magic Control Technology Corp.");
+ IDS.put(1816, "Imation Corp.");
+ IDS.put(1838, "Sunix Co., Ltd.");
+ IDS.put(1846, "Lorom Industrial Co., Ltd.");
+ IDS.put(1848, "Mad Catz, Inc.");
+ IDS.put(1899, "HID Global GmbH");
+ IDS.put(1901, "Denso Corporation");
+ IDS.put(1913, "Fairchild Semiconductor");
+ IDS.put(1921, "SanDisk Corporation");
+ IDS.put(1937, "Copartner Technology Corporation");
+ IDS.put(1954, "National Technical Systems");
+ IDS.put(1971, "Plustek, Inc.");
+ IDS.put(1972, "OLYMPUS CORPORATION");
+ IDS.put(1975, "TIME Interconnect Ltd.");
+ IDS.put(1994, "AVerMedia Technologies, Inc.");
+ IDS.put(1999, "Casio Computer Co., Ltd.");
+ IDS.put(2015, "David Electronics Company, Ltd.");
+ IDS.put(2039, "Century Corporation");
+ IDS.put(2058, "Evermuch Technology Co., Ltd.");
+ IDS.put(2101, "Action Star Enterprise Co., Ltd.");
+ IDS.put(2112, "Argosy Research Inc.");
+ IDS.put(2122, "Wipro Limited");
+ IDS.put(2159, "MEC IMEX INC/HPT");
+ IDS.put(2205, "Icron Technologies Corporation");
+ IDS.put(2247, "TAI TWUN ENTERPRISE CO., LTD.");
+ IDS.put(2276, "Pioneer Corporation");
+ IDS.put(2278, "Gemalto SA");
+ IDS.put(2310, "FARADAY Technology Corp.");
+ IDS.put(2313, "Audio-Technica Corp.");
+ IDS.put(2316, "Silicon Motion, Inc. - Taiwan");
+ IDS.put(2334, "Garmin International");
+ IDS.put(2352, "Toshiba Corporation");
+ IDS.put(2362, "Pixart Imaging, Inc.");
+ IDS.put(2363, "Plextor LLC");
+ IDS.put(2366, "J.S.T. Mfg. Co., Ltd.");
+ IDS.put(2385, "Kingston Technology Company");
+ IDS.put(2389, "NVIDIA");
+ IDS.put(2395, "Medialogic Corporation");
+ IDS.put(2397, "Polycom, Inc.");
+ IDS.put(2468, "Contech Research, Inc.");
+ IDS.put(2472, "Lin Shiung Enterprise Co., Ltd.");
+ IDS.put(2475, "Japan Cash Machine Co., Ltd.");
+ IDS.put(2498, "NISCA Corporation");
+ IDS.put(2511, "Electronics Testing Center, Taiwan");
+ IDS.put(2522, "A-FOUR TECH CO., LTD.");
+ IDS.put(2555, "Altera");
+ IDS.put(2578, "Cambridge Silicon Radio Ltd.");
+ IDS.put(2583, "HOYA Corporation");
+ IDS.put(2631, "Hirose Electric Co., Ltd.");
+ IDS.put(2636, "COMPUTEX Co., Ltd.");
+ IDS.put(2640, "Mimaki Engineering Co., Ltd.");
+ IDS.put(2652, "Broadcom Corp.");
+ IDS.put(2667, "Green House Co., Ltd.");
+ IDS.put(2702, "Japan Aviation Electronics Industry Ltd. (JAE)");
+ IDS.put(2727, "Wincor Nixdorf GmbH & Co KG");
+ IDS.put(2733, "Rohde & Schwarz GmbH & Co. KG");
+ IDS.put(2787, "Allion Labs, Inc.");
+ IDS.put(2821, "ASUSTek Computer Inc.");
+ IDS.put(2849, "Yokogawa Electric Corporation");
+ IDS.put(2851, "Pan-Asia Electronics Co., Ltd.");
+ IDS.put(2894, "Musical Electronics Ltd.");
+ IDS.put(2907, "Anritsu Corporation");
+ IDS.put(2922, "Maxim Integrated Products");
+ IDS.put(2965, "ASIX Electronics Corporation");
+ IDS.put(2967, "O2Micro, Inc.");
+ IDS.put(3010, "Seagate Technology LLC");
+ IDS.put(3034, "Realtek Semiconductor Corp.");
+ IDS.put(3035, "Ericsson AB");
+ IDS.put(3044, "Elka International Ltd.");
+ IDS.put(3056, "Pace Micro Technology PLC");
+ IDS.put(3108, "Taiyo Yuden Co., Ltd.");
+ IDS.put(3129, "Aeroflex");
+ IDS.put(3132, "Radius Co., Ltd.");
+ IDS.put(3141, "Sonix Technology Co., Ltd.");
+ IDS.put(3158, "Billion Bright (HK) Corporation Limited");
+ IDS.put(3161, "Dong Guan Shinko Wire Co., Ltd.");
+ IDS.put(3170, "Chant Sincere Co., Ltd");
+ IDS.put(3190, "Solid State System Co., Ltd.");
+ IDS.put(3209, "Honda Tsushin Kogyo Co., Ltd");
+ IDS.put(3245, "Motorola Solutions");
+ IDS.put(3255, "Singatron Enterprise Co. Ltd.");
+ IDS.put(3268, "emsys Embedded Systems GmbH");
+ IDS.put(32902, "Intel Corporation");
+ IDS.put(3294, "Z-Com INC.");
+ IDS.put(3313, "e-CONN ELECTRONIC CO., LTD.");
+ IDS.put(3314, "ENE Technology Inc.");
+ IDS.put(3351, "NALTEC, Inc.");
+ IDS.put(3402, "NF Corporation");
+ IDS.put(3403, "Grape Systems Inc.");
+ IDS.put(3409, "Volex (Asia) Pte Ltd");
+ IDS.put(3425, "MEILU ELECTRONICS (SHENZHEN) CO., LTD.");
+ IDS.put(3441, "Hirakawa Hewtech Corp.");
+ IDS.put(3452, "Taiwan Line Tek Electronic Co., Ltd.");
+ IDS.put(3463, "Dolby Laboratories Inc.");
+ IDS.put(3468, "C-MEDIA ELECTRONICS INC.");
+ IDS.put(3472, "Sure-Fire Electrical Corporation");
+ IDS.put(3495, "IOGEAR, Inc.");
+ IDS.put(3504, "Micro-Star International Co., Ltd.");
+ IDS.put(3537, "Contek Electronics Co., Ltd.");
+ IDS.put(3540, "Custom Engineering SPA");
+ IDS.put(3641, "Smart Modular Technologies, Inc.");
+ IDS.put(3658, "Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.");
+ IDS.put(3673, "Bourns, Inc.");
+ IDS.put(3690, "Megawin Technology Co., Ltd.");
+ IDS.put(3698, "Hsi-Chin Electronics Co., Ltd.");
+ IDS.put(3714, "Ching Tai Electric Wire & Cable Co., Ltd.");
+ IDS.put(3724, "Well Force Electronic Co., Ltd");
+ IDS.put(3725, "MediaTek Inc.");
+ IDS.put(3728, "CRU");
+ IDS.put(3744, "Ours Technology Inc.");
+ IDS.put(3762, "Y-S ELECTRONIC CO., LTD.");
+ IDS.put(3778, "Sweetray Industrial Ltd.");
+ IDS.put(3779, "Axell Corporation");
+ IDS.put(3782, "InnoVISION Multimedia Limited");
+ IDS.put(3790, "TaiSol Electronics Co., Ltd.");
+ IDS.put(3812, "Sunrich Technology (H.K.) Ltd.");
+ IDS.put(3868, "Funai Electric Co., Ltd.");
+ IDS.put(3873, "IOI Technology Corporation");
+ IDS.put(3890, "YFC-BonEagle Electric Co., Ltd.");
+ IDS.put(3896, "Nien-Yi Industrial Corp.");
+ IDS.put(3916, "WORLDWIDE CABLE OPTO CORP.");
+ IDS.put(3923, "Taiyo Cable (Dongguan) Co. Ltd.");
+ IDS.put(3924, "Kawai Musical Instruments Mfg. Co., Ltd.");
+ IDS.put(3936, "GuangZhou Chief Tech Electronic Technology Co. Ltd.");
+ IDS.put(3944, "UQUEST, LTD.");
+ IDS.put(3991, "CviLux Corporation");
+ IDS.put(4003, "Chief Land Electronic Co., Ltd.");
+ IDS.put(4046, "Sony Mobile Communications");
+ IDS.put(4087, "CHI SHING COMPUTER ACCESSORIES CO., LTD.");
+ IDS.put(4096, "Speed Tech Corp.");
+ IDS.put(4100, "LG Electronics Inc.");
+ IDS.put(4101, "Apacer Technology Inc.");
+ IDS.put(4134, "Newly Corporation");
+ IDS.put(4168, "Targus Group International");
+ IDS.put(4172, "AMCO TEC International Inc.");
+ IDS.put(4183, "ON Semiconductor");
+ IDS.put(4184, "Western Digital Technologies, Inc.");
+ IDS.put(4227, "CANON ELECTRONICS INC.");
+ IDS.put(4235, "Grand-tek Technology Co., Ltd.");
+ IDS.put(4236, "Robert Bosch GmbH");
+ IDS.put(4238, "Lotes Co., Ltd.");
+ IDS.put(4266, "Cables To Go");
+ IDS.put(4267, "Universal Global Scientific Industrial Co., Ltd.");
+ IDS.put(4292, "Silicon Laboratories, Inc.");
+ IDS.put(4301, "Kycon Inc.");
+ IDS.put(4362, "Moxa Inc.");
+ IDS.put(4370, "Golden Bright (Sichuan) Electronic Technology Co Ltd");
+ IDS.put(4382, "VSO ELECTRONICS CO., LTD.");
+ IDS.put(4398, "Master Hill Electric Wire and Cable Co., Ltd.");
+ IDS.put(4477, "Santa Electronic Inc.");
+ IDS.put(4505, "Sierra Wireless Inc.");
+ IDS.put(4522, "GlobalMedia Group, LLC");
+ IDS.put(4528, "ATECH FLASH TECHNOLOGY");
+ IDS.put(4643, "SKYCABLE ENTERPRISE CO., LTD.");
+ IDS.put(4703, "ADATA Technology Co., Ltd.");
+ IDS.put(4716, "Aristocrat Technologies");
+ IDS.put(4717, "Bel Stewart");
+ IDS.put(4742, "MARVELL SEMICONDUCTOR, INC.");
+ IDS.put(4756, "RISO KAGAKU CORP.");
+ IDS.put(4792, "Zhejiang Xinya Electronic Technology Co., Ltd.");
+ IDS.put(4817, "Huawei Technologies Co., Ltd.");
+ IDS.put(4823, "Better Holdings (HK) Limited");
+ IDS.put(4907, "Konica Minolta, Inc.");
+ IDS.put(4925, "Jasco Products Company");
+ IDS.put(4989, "Pericom Semiconductor Corp.");
+ IDS.put(5008, "TomTom International B.V.");
+ IDS.put(5075, "AzureWave Technologies, Inc.");
+ IDS.put(5117, "Initio Corporation");
+ IDS.put(5118, "Phison Electronics Corp.");
+ IDS.put(5134, "Telechips, Inc.");
+ IDS.put(5145, "ABILITY ENTERPRISE CO., LTD.");
+ IDS.put(5148, "Leviton Manufacturing");
+ IDS.put(5271, "Panstrong Company Ltd.");
+ IDS.put(5293, "CTK Corporation");
+ IDS.put(5296, "StarTech.com Ltd.");
+ IDS.put(5376, "Ellisys");
+ IDS.put(5404, "VeriSilicon Holdings Co., Ltd.");
+ IDS.put(5421, "JMicron Technology Corp.");
+ IDS.put(5422, "HLDS (Hitachi-LG Data Storage, Inc.)");
+ IDS.put(5440, "Phihong Technology Co., Ltd.");
+ IDS.put(5451, "PNY Technologies Inc.");
+ IDS.put(5453, "Rapid Conn, Connect County Holdings Bhd");
+ IDS.put(5454, "D & M Holdings, Inc.");
+ IDS.put(5480, "Sunf Pu Technology Co., Ltd");
+ IDS.put(5488, "ALLTOP TECHNOLOGY CO., LTD.");
+ IDS.put(5510, "Palconn Technology Co., Ltd.");
+ IDS.put(5528, "Kunshan Guoji Electronics Co., Ltd.");
+ IDS.put(5546, "DongGuan Ya Lian Electronics Co., Ltd.");
+ IDS.put(5645, "Samtec");
+ IDS.put(5694, "HongLin Electronics Co., Ltd.");
+ IDS.put(5753, "Total Phase");
+ IDS.put(5766, "ZOOM Corporation");
+ IDS.put(5836, "silex technology, Inc.");
+ IDS.put(5946, "F. Hoffmann-La Roche AG");
+ IDS.put(5960, "MQP Electronics Ltd.");
+ IDS.put(5964, "ASMedia Technology Inc.");
+ IDS.put(5998, "UD electronic corp.");
+ IDS.put(6001, "Shenzhen Alex Connector Co., Ltd.");
+ IDS.put(6002, "System Level Solutions, Inc.");
+ IDS.put(6018, "Spreadtrum Hong Kong Limited");
+ IDS.put(6024, "ShenZhen Litkconn Technology Co., Ltd.");
+ IDS.put(6053, "Advanced Connection Technology Inc.");
+ IDS.put(6095, "Hip Hing Cable & Plug Mfy. Ltd.");
+ IDS.put(6121, "DisplayLink (UK) Ltd.");
+ IDS.put(6127, "Lenovo");
+ IDS.put(6133, "K.K. Rocky");
+ IDS.put(6160, "Wanshih Electronic Co., Ltd.");
+ IDS.put(6185, "Dongguan YuQiu Electronics Co., Ltd.");
+ IDS.put(6193, "Gwo Jinn Industries Co., Ltd.");
+ IDS.put(6297, "Linkiss Co., Ltd.");
+ IDS.put(6353, "Google Inc.");
+ IDS.put(6394, "Kuang Ying Computer Equipment Co., Ltd.");
+ IDS.put(6421, "Nordic Semiconductor ASA");
+ IDS.put(6448, "Shenzhen Xianhe Technology Co., Ltd.");
+ IDS.put(6449, "Ningbo Broad Telecommunication Co., Ltd.");
+ IDS.put(6470, "Irisguard UK Ltd");
+ IDS.put(6473, "Lab126");
+ IDS.put(6481, "Hyperstone GmbH");
+ IDS.put(6487, "BIOS Corporation");
+ IDS.put(6626, "Solomon Systech Limited");
+ IDS.put(6639, "Pak Heng Technology (Shenzhen) Co., Ltd.");
+ IDS.put(6655, "Best Buy China Ltd.");
+ IDS.put(6666, "USB-IF non-workshop");
+ IDS.put(6709, "Artesyn Technologies Inc.");
+ IDS.put(6720, "TERMINUS TECHNOLOGY INC.");
+ IDS.put(6766, "Global Unichip Corp.");
+ IDS.put(6786, "Proconn Technology Co., Ltd.");
+ IDS.put(6794, "Simula Technology Inc.");
+ IDS.put(6795, "SGS Taiwan Ltd.");
+ IDS.put(6830, "Johnson Component & Equipments Co., Ltd.");
+ IDS.put(6834, "Allied Vision Technologies GmbH");
+ IDS.put(6859, "Salcomp Plc");
+ IDS.put(6865, "Desan Wire Co., Ltd.");
+ IDS.put(6944, "MStar Semiconductor, Inc.");
+ IDS.put(6984, "Plastron Precision Co., Ltd.");
+ IDS.put(7013, "The Hong Kong Standards and Testing Centre Ltd.");
+ IDS.put(7048, "ShenMing Electron (Dong Guan) Co., Ltd.");
+ IDS.put(7086, "Vuzix Corporation");
+ IDS.put(7108, "Ford Motor Co.");
+ IDS.put(7118, "Contac Cable Industrial Limited");
+ IDS.put(7119, "Sunplus Innovation Technology Inc.");
+ IDS.put(7120, "Hangzhou Riyue Electronics Co., Ltd.");
+ IDS.put(7158, "Orient Semiconductor Electronics, Ltd.");
+ IDS.put(7207, "SHENZHEN DNS INDUSTRIES CO., LTD.");
+ IDS.put(7217, "LS Mtron Ltd.");
+ IDS.put(7229, "NONIN MEDICAL INC.");
+ IDS.put(7275, "Philips & Lite-ON Digital Solutions Corporation");
+ IDS.put(7310, "ASTRON INTERNATIONAL CORP.");
+ IDS.put(7320, "ALPINE ELECTRONICS, INC.");
+ IDS.put(7347, "Aces Electronics Co., Ltd.");
+ IDS.put(7348, "OPEX CORPORATION");
+ IDS.put(7390, "Telecommunications Technology Association (TTA)");
+ IDS.put(7434, "Visteon Corporation");
+ IDS.put(7465, "Horng Tong Enterprise Co., Ltd.");
+ IDS.put(7501, "Pegatron Corporation");
+ IDS.put(7516, "Fresco Logic Inc.");
+ IDS.put(7529, "Walta Electronic Co., Ltd.");
+ IDS.put(7543, "Yueqing Changling Electronic Instrument Corp., Ltd.");
+ IDS.put(7584, "Parade Technologies, Inc.");
+ IDS.put(7647, "L&T Technology Services");
+ IDS.put(7649, "Actions Microelectronics Co., Ltd.");
+ IDS.put(7666, "China Telecommunication Technology Labs - Terminals");
+ IDS.put(7668, "SHEN ZHEN FORMAN PRECISION INDUSTRY CO., LTD.");
+ IDS.put(7682, "GLOBEMASTER TECHNOLOGIES CO., LTD.");
+ IDS.put(7696, "Point Grey Research Inc.");
+ IDS.put(7751, "HUNG TA H.T.ENTERPRISE CO., LTD.");
+ IDS.put(7758, "Etron Technology, Inc.");
+ IDS.put(7795, "COMLINK ELECTRONICS CO., LTD.");
+ IDS.put(7818, "HIBEST Electronic (DongGuan) Co., Ltd.");
+ IDS.put(7825, "Other World Computing");
+ IDS.put(7863, "WIN WIN PRECISION INDUSTRIAL CO., LTD.");
+ IDS.put(7879, "Gefen Inc.");
+ IDS.put(7881, "MOSER BAER INDIA LIMITED");
+ IDS.put(7898, "AIRTIES WIRELESS NETWORKS");
+ IDS.put(7956, "Astoria Networks GmbH");
+ IDS.put(7969, "Scosche Industries");
+ IDS.put(7976, "Cal-Comp Electronics & Communications");
+ IDS.put(7977, "Analogix Semiconductor, Inc.");
+ IDS.put(7989, "Amphenol ShouhMin Industry (ShenZhen) Co., Ltd");
+ IDS.put(7996, "Chang Yang Electronics Company Ltd.");
+ IDS.put(8073, "Dongguan Goldconn Electronics Co., Ltd.");
+ IDS.put(8074, "Morning Star Industrial Co., Ltd.");
+ IDS.put(8117, "Unify Software and Solutions GmbH & Co. KG");
+ IDS.put(8137, "NXP Semiconductors");
+ IDS.put(8181, "Changzhou Wujin BEST Electronic Cables Co., Ltd.");
+ IDS.put(8205, "Belkin Electronic (Changzhou) Co., Ltd.");
+ IDS.put(8220, "Freeport Resources Enterprises Corp.");
+ IDS.put(8222, "Qingdao Haier Telecom Co., Ltd.");
+ IDS.put(8284, "Shenzhen Tronixin Electronics Co., Ltd.");
+ IDS.put(8294, "Unicorn Electronics Components Co., Ltd.");
+ IDS.put(8334, "Luxshare-ICT");
+ IDS.put(8341, "CE LINK LIMITED");
+ IDS.put(8342, "Microconn Electronic Co., Ltd.");
+ IDS.put(8367, "Shenzhen CARVE Electronics Co., Ltd.");
+ IDS.put(8382, "BURY GmbH & Co. KG");
+ IDS.put(8384, "FENGHUA KINGSUN CO., LTD.");
+ IDS.put(8386, "Sumitomo Electric Ind., Ltd., Optical Comm. R&D Lab");
+ IDS.put(8439, "XIMEA s.r.o.");
+ IDS.put(8457, "VIA Labs, Inc.");
+ IDS.put(8492, "Shenzhen Linoya Electronic Co., Ltd.");
+ IDS.put(8494, "Amphenol AssembleTech (Xiamen) Co., Ltd.");
+ IDS.put(8524, "Y Soft Corporation");
+ IDS.put(8550, "JVC KENWOOD Corporation");
+ IDS.put(8564, "Transcend Information, Inc.");
+ IDS.put(8566, "TMC/Allion Test Labs");
+ IDS.put(8613, "Genesis Technology USA, Inc.");
+ IDS.put(8627, "Dongguan Teconn Electronics Technology Co., Ltd.");
+ IDS.put(8644, "Netcom Technology (HK) Limited");
+ IDS.put(8659, "Compupack Technology Co., Ltd.");
+ IDS.put(8667, "G-Max Technology Co., Ltd.");
+ IDS.put(8679, "Sagemcom Broadband SAS");
+ IDS.put(8695, "Wuerth-Elektronik eiSos GmbH & Co. KG");
+ IDS.put(8707, "Shin Shin Co., Ltd.");
+ IDS.put(8709, "3eYamaichi Electronics Co., Ltd.");
+ IDS.put(8710, "Wiretek International Investment Ltd.");
+ IDS.put(8711, "Fuzhou Rockchip Electronics Co., Ltd.");
+ IDS.put(8752, "Plugable Technologies");
+ IDS.put(8756, "T-CONN PRECISION CORPORATION");
+ IDS.put(8831, "Granite River Labs");
+ IDS.put(8842, "Hotron Precision Electronic Ind. Corp.");
+ IDS.put(8875, "Trigence Semiconductor, Inc.");
+ IDS.put(8888, "Motorola Mobility Inc.");
+ IDS.put(8904, "Karming Electronic (Shenzhen) Co., Ltd.");
+ IDS.put(8981, "Avery Design Systems, Inc.");
+ IDS.put(8993, "iKingdom Corp. (d.b.a. iConnectivity)");
+ IDS.put(9051, "KangXiang Electronic Co., Ltd.");
+ IDS.put(9068, "ZheJiang Chunsheng Electronics Co., Ltd.");
+ IDS.put(9130, "DOK (HK) Trading Limited");
+ IDS.put(9132, "Marunix Electron Limited");
+ IDS.put(9165, "Avconn Precise Connector Co., Ltd.");
+ IDS.put(9184, "BitifEye Digital Test Solutions GmbH");
+ IDS.put(9205, "Speed Conn Co., Ltd.");
+ IDS.put(9222, "INSIDE Secure");
+ IDS.put(9292, "Minebea Co., Ltd.");
+ IDS.put(9299, "BAANTO");
+ IDS.put(9338, "Suzhou Jutze Technologies Co., Ltd");
+ IDS.put(9355, "DONGGUAN SYNCONN PRECISION INDUSTRY CO. LTD.");
+ IDS.put(9382, "Shenzhen Pangngai Industrial Co., Ltd.");
+ IDS.put(9422, "Shenzhen Deren Electronic Co., Ltd.");
+ IDS.put(9424, "Smith Micro Software, Inc.");
+ IDS.put(9453, "ZEN FACTORY GROUP (ASIA) LTD.");
+ IDS.put(9481, "Chain-In Electronic Co., Ltd.");
+ IDS.put(9514, "SUZHOU KELI TECHNOLOGY DEVELOPMENT CO., LTD.");
+ IDS.put(9515, "TOP Exactitude Industry (ShenZhen) Co., Ltd.");
+ IDS.put(9525, "ShenZhen Hogend Precision Technology Co., Ltd.");
+ IDS.put(9527, "Norel Systems Ltd.");
+ IDS.put(9556, "ASSA ABLOY AB");
+ IDS.put(9575, "DongGuan LongTao Electronic Co., Ltd.");
+ IDS.put(9577, "DongGuan City MingJi Electronics Co., Ltd.");
+ IDS.put(9589, "Weida Hi-Tech Co., Ltd.");
+ IDS.put(9593, "Dongguan Wisechamp Electronic Co., Ltd.");
+ IDS.put(9613, "Sequans Communications");
+ IDS.put(9636, "ALGOLTEK, INC.");
+ IDS.put(9651, "DongGuan Elinke Industrial Co., Ltd.");
+ IDS.put(9679, "Corning Optical Communications LLC");
+ IDS.put(9714, "Dongguan Jinyue Electronics Co., Ltd.");
+ IDS.put(9723, "RICOH IMAGING COMPANY, LTD.");
+ IDS.put(9742, "DongGuan HYX Industrial Co., Ltd.");
+ IDS.put(9753, "Advanced Silicon SA");
+ IDS.put(9756, "EISST Limited");
+ IDS.put(9771, "YTOP Electronics Technical (Kunshan) Co., Ltd.");
+ IDS.put(9841, "Innovative Logic");
+ IDS.put(9842, "GoPro");
+ IDS.put(9846, "Basler AG");
+ IDS.put(9851, "Palpilot International Corp.");
+ IDS.put(9896, "UNIREX CORPORATION");
+ IDS.put(9917, "Integral Memory Plc.");
+ IDS.put(9973, "Morning Star Digital Connector Co., Ltd.");
+ IDS.put(9984, "MITACHI CO., LTD.");
+ IDS.put(9999, "HGST, a Western Digital Company");
+ }
+}
diff --git a/libuvc/src/main/java/com/serenegiant/usb/UVCCamera.java b/libuvc/src/main/java/com/serenegiant/usb/UVCCamera.java
new file mode 100644
index 0000000000000000000000000000000000000000..08dc73cc27d6af2913c971ca6341dda254fdf9b2
--- /dev/null
+++ b/libuvc/src/main/java/com/serenegiant/usb/UVCCamera.java
@@ -0,0 +1,1262 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.usb.UsbDevice;
+import android.text.TextUtils;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.serenegiant.usb.USBMonitor.UsbControlBlock;
+import com.serenegiant.utils.XLogWrapper;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UVCCamera {
+ public static boolean DEBUG = false; // TODO set false when releasing
+ private static final String TAG = UVCCamera.class.getSimpleName();
+ private static final String DEFAULT_USBFS = "/dev/bus/usb";
+
+ public static final int DEFAULT_PREVIEW_MODE = 0;
+ public static final int DEFAULT_PREVIEW_MIN_FPS = 1;
+ public static final int DEFAULT_PREVIEW_MAX_FPS = 31;
+ public static final float DEFAULT_BANDWIDTH = 1.0f;
+
+ public static final int FRAME_FORMAT_YUYV = 0;
+ public static final int FRAME_FORMAT_MJPEG = 1;
+
+ public static final int PIXEL_FORMAT_RAW = 0;
+ public static final int PIXEL_FORMAT_YUV = 1;
+ public static final int PIXEL_FORMAT_RGB565 = 2;
+ public static final int PIXEL_FORMAT_RGBX = 3;
+ public static final int PIXEL_FORMAT_YUV420SP = 4; // NV12
+ public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar,NV21,但是保存到jpg颜色失真
+
+ //--------------------------------------------------------------------------------
+ public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode
+ public static final int CTRL_AE = 0x00000002; // D1: Auto-Exposure Mode
+ public static final int CTRL_AE_PRIORITY = 0x00000004; // D2: Auto-Exposure Priority
+ public static final int CTRL_AE_ABS = 0x00000008; // D3: Exposure Time (Absolute)
+ public static final int CTRL_AR_REL = 0x00000010; // D4: Exposure Time (Relative)
+ public static final int CTRL_FOCUS_ABS = 0x00000020; // D5: Focus (Absolute)
+ public static final int CTRL_FOCUS_REL = 0x00000040; // D6: Focus (Relative)
+ public static final int CTRL_IRIS_ABS = 0x00000080; // D7: Iris (Absolute)
+ public static final int CTRL_IRIS_REL = 0x00000100; // D8: Iris (Relative)
+ public static final int CTRL_ZOOM_ABS = 0x00000200; // D9: Zoom (Absolute)
+ public static final int CTRL_ZOOM_REL = 0x00000400; // D10: Zoom (Relative)
+ public static final int CTRL_PANTILT_ABS = 0x00000800; // D11: PanTilt (Absolute)
+ public static final int CTRL_PANTILT_REL = 0x00001000; // D12: PanTilt (Relative)
+ public static final int CTRL_ROLL_ABS = 0x00002000; // D13: Roll (Absolute)
+ public static final int CTRL_ROLL_REL = 0x00004000; // D14: Roll (Relative)
+ public static final int CTRL_FOCUS_AUTO = 0x00020000; // D17: Focus, Auto
+ public static final int CTRL_PRIVACY = 0x00040000; // D18: Privacy
+ public static final int CTRL_FOCUS_SIMPLE = 0x00080000; // D19: Focus, Simple
+ public static final int CTRL_WINDOW = 0x00100000; // D20: Window
+
+ public static final int PU_BRIGHTNESS = 0x80000001; // D0: Brightness
+ public static final int PU_CONTRAST = 0x80000002; // D1: Contrast
+ public static final int PU_HUE = 0x80000004; // D2: Hue
+ public static final int PU_SATURATION = 0x80000008; // D3: Saturation
+ public static final int PU_SHARPNESS = 0x80000010; // D4: Sharpness
+ public static final int PU_GAMMA = 0x80000020; // D5: Gamma
+ public static final int PU_WB_TEMP = 0x80000040; // D6: White Balance Temperature
+ public static final int PU_WB_COMPO = 0x80000080; // D7: White Balance Component
+ public static final int PU_BACKLIGHT = 0x80000100; // D8: Backlight Compensation
+ public static final int PU_GAIN = 0x80000200; // D9: Gain
+ public static final int PU_POWER_LF = 0x80000400; // D10: Power Line Frequency
+ public static final int PU_HUE_AUTO = 0x80000800; // D11: Hue, Auto
+ public static final int PU_WB_TEMP_AUTO = 0x80001000; // D12: White Balance Temperature, Auto
+ public static final int PU_WB_COMPO_AUTO = 0x80002000; // D13: White Balance Component, Auto
+ public static final int PU_DIGITAL_MULT = 0x80004000; // D14: Digital Multiplier
+ public static final int PU_DIGITAL_LIMIT = 0x80008000; // D15: Digital Multiplier Limit
+ public static final int PU_AVIDEO_STD = 0x80010000; // D16: AnaXLogWrapper Video Standard
+ public static final int PU_AVIDEO_LOCK = 0x80020000; // D17: AnaXLogWrapper Video Lock Status
+ public static final int PU_CONTRAST_AUTO = 0x80040000; // D18: Contrast, Auto
+
+ // uvc_status_class from libuvc.h
+ public static final int STATUS_CLASS_CONTROL = 0x10;
+ public static final int STATUS_CLASS_CONTROL_CAMERA = 0x11;
+ public static final int STATUS_CLASS_CONTROL_PROCESSING = 0x12;
+
+ // uvc_status_attribute from libuvc.h
+ public static final int STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00;
+ public static final int STATUS_ATTRIBUTE_INFO_CHANGE = 0x01;
+ public static final int STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02;
+ public static final int STATUS_ATTRIBUTE_UNKNOWN = 0xff;
+
+ private static boolean isLoaded;
+ static {
+ if (!isLoaded) {
+ System.loadLibrary("jpeg-turbo1500");
+ System.loadLibrary("usb100");
+ System.loadLibrary("uvc");
+ System.loadLibrary("UVCCamera");
+ isLoaded = true;
+ }
+ }
+
+ private UsbControlBlock mCtrlBlock;
+ protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ
+ protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ
+ protected int mCurrentFrameFormat = FRAME_FORMAT_MJPEG;
+ protected int mCurrentWidth = 640, mCurrentHeight = 480;
+ protected float mCurrentBandwidthFactor = DEFAULT_BANDWIDTH;
+ protected String mSupportedSize;
+ protected List mCurrentSizeList;
+ // these fields from here are accessed from native code and do not change name and remove
+ protected long mNativePtr;
+ protected int mScanningModeMin, mScanningModeMax, mScanningModeDef;
+ protected int mExposureModeMin, mExposureModeMax, mExposureModeDef;
+ protected int mExposurePriorityMin, mExposurePriorityMax, mExposurePriorityDef;
+ protected int mExposureMin, mExposureMax, mExposureDef;
+ protected int mAutoFocusMin, mAutoFocusMax, mAutoFocusDef;
+ protected int mFocusMin, mFocusMax, mFocusDef;
+ protected int mFocusRelMin, mFocusRelMax, mFocusRelDef;
+ protected int mFocusSimpleMin, mFocusSimpleMax, mFocusSimpleDef;
+ protected int mIrisMin, mIrisMax, mIrisDef;
+ protected int mIrisRelMin, mIrisRelMax, mIrisRelDef;
+ protected int mPanMin, mPanMax, mPanDef;
+ protected int mTiltMin, mTiltMax, mTiltDef;
+ protected int mRollMin, mRollMax, mRollDef;
+ protected int mPanRelMin, mPanRelMax, mPanRelDef;
+ protected int mTiltRelMin, mTiltRelMax, mTiltRelDef;
+ protected int mRollRelMin, mRollRelMax, mRollRelDef;
+ protected int mPrivacyMin, mPrivacyMax, mPrivacyDef;
+ protected int mAutoWhiteBlanceMin, mAutoWhiteBlanceMax, mAutoWhiteBlanceDef;
+ protected int mAutoWhiteBlanceCompoMin, mAutoWhiteBlanceCompoMax, mAutoWhiteBlanceCompoDef;
+ protected int mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef;
+ protected int mWhiteBlanceCompoMin, mWhiteBlanceCompoMax, mWhiteBlanceCompoDef;
+ protected int mWhiteBlanceRelMin, mWhiteBlanceRelMax, mWhiteBlanceRelDef;
+ protected int mBacklightCompMin, mBacklightCompMax, mBacklightCompDef;
+ protected int mBrightnessMin, mBrightnessMax, mBrightnessDef;
+ protected int mContrastMin, mContrastMax, mContrastDef;
+ protected int mSharpnessMin, mSharpnessMax, mSharpnessDef;
+ protected int mGainMin, mGainMax, mGainDef;
+ protected int mGammaMin, mGammaMax, mGammaDef;
+ protected int mSaturationMin, mSaturationMax, mSaturationDef;
+ protected int mHueMin, mHueMax, mHueDef;
+ protected int mZoomMin, mZoomMax, mZoomDef;
+ protected int mZoomRelMin, mZoomRelMax, mZoomRelDef;
+ protected int mPowerlineFrequencyMin, mPowerlineFrequencyMax, mPowerlineFrequencyDef;
+ protected int mMultiplierMin, mMultiplierMax, mMultiplierDef;
+ protected int mMultiplierLimitMin, mMultiplierLimitMax, mMultiplierLimitDef;
+ protected int mAnaXLogWrapperVideoStandardMin, mAnaXLogWrapperVideoStandardMax, mAnaXLogWrapperVideoStandardDef;
+ protected int mAnaXLogWrapperVideoLockStateMin, mAnaXLogWrapperVideoLockStateMax, mAnaXLogWrapperVideoLockStateDef;
+ // until here
+ /**
+ * the sonctructor of this class should be call within the thread that has a looper
+ * (UI thread or a thread that called Looper.prepare)
+ */
+ public UVCCamera() {
+ mNativePtr = nativeCreate();
+ mSupportedSize = null;
+ }
+
+ /**
+ * connect to a UVC camera
+ * USB permission is necessary before this method is called
+ * @param ctrlBlock
+ */
+ public synchronized void open(final UsbControlBlock ctrlBlock) {
+ int result = -2;
+ StringBuilder sb = new StringBuilder();
+ try {
+ mCtrlBlock = ctrlBlock.clone();
+ result = nativeConnect(mNativePtr,
+ mCtrlBlock.getVenderId(), mCtrlBlock.getProductId(),
+ mCtrlBlock.getFileDescriptor(),
+ mCtrlBlock.getBusNum(),
+ mCtrlBlock.getDevNum(),
+ getUSBFSName(mCtrlBlock));
+ sb.append("调用nativeConnect返回值:"+result);
+// long id_camera, int venderId, int productId, int fileDescriptor, int busNum, int devAddr, String usbfs
+ } catch (final Exception e) {
+ XLogWrapper.w(TAG, e);
+ for(int i = 0; i< e.getStackTrace().length; i++){
+ sb.append(e.getStackTrace()[i].toString());
+ sb.append("\n");
+ }
+ sb.append("core message ->"+e.getLocalizedMessage());
+ result = -1;
+ }
+
+ if (result != 0) {
+ throw new UnsupportedOperationException("open failed:result=" + result+"----->" +
+ "id_camera="+mNativePtr+";venderId="+mCtrlBlock.getVenderId()
+ +";productId="+mCtrlBlock.getProductId()+";fileDescriptor="+mCtrlBlock.getFileDescriptor()
+ +";busNum="+mCtrlBlock.getBusNum()+";devAddr="+mCtrlBlock.getDevNum()
+ +";usbfs="+getUSBFSName(mCtrlBlock)+"\n"+"Exception:"+sb.toString());
+ }
+
+ if (mNativePtr != 0 && TextUtils.isEmpty(mSupportedSize)) {
+ mSupportedSize = nativeGetSupportedSize(mNativePtr);
+ }
+ List supportedSizes = getSupportedSizeList();
+ if (!supportedSizes.isEmpty()) {
+ mCurrentWidth = supportedSizes.get(0).width;
+ mCurrentHeight = supportedSizes.get(0).height;
+ }
+ nativeSetPreviewSize(mNativePtr, mCurrentWidth, mCurrentHeight,
+ DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, DEFAULT_PREVIEW_MODE, DEFAULT_BANDWIDTH);
+ }
+
+ /**
+ * set status callback
+ * @param callback
+ */
+ public void setStatusCallback(final IStatusCallback callback) {
+ if (mNativePtr != 0) {
+ nativeSetStatusCallback(mNativePtr, callback);
+ }
+ }
+
+ /**
+ * set button callback
+ * @param callback
+ */
+ public void setButtonCallback(final IButtonCallback callback) {
+ if (mNativePtr != 0) {
+ nativeSetButtonCallback(mNativePtr, callback);
+ }
+ }
+
+ /**
+ * close and release UVC camera
+ */
+ public synchronized void close() {
+ stopPreview();
+ if (mNativePtr != 0) {
+ nativeRelease(mNativePtr);
+// mNativePtr = 0; // nativeDestroyを呼ぶのでここでクリアしちゃダメ
+ }
+ if (mCtrlBlock != null) {
+ mCtrlBlock.close();
+ mCtrlBlock = null;
+ }
+ mControlSupports = mProcSupports = 0;
+ mCurrentFrameFormat = -1;
+ mCurrentBandwidthFactor = 0;
+ mSupportedSize = null;
+ mCurrentSizeList = null;
+ if (DEBUG) XLogWrapper.v(TAG, "close:finished");
+ }
+
+ public UsbDevice getDevice() {
+ return mCtrlBlock != null ? mCtrlBlock.getDevice() : null;
+ }
+
+ public String getDeviceName(){
+ return mCtrlBlock != null ? mCtrlBlock.getDeviceName() : null;
+ }
+
+ public UsbControlBlock getUsbControlBlock() {
+ return mCtrlBlock;
+ }
+
+ public synchronized String getSupportedSize() {
+ return !TextUtils.isEmpty(mSupportedSize) ? mSupportedSize : (mSupportedSize = nativeGetSupportedSize(mNativePtr));
+ }
+
+ public Size getPreviewSize() {
+ Size result = null;
+ final List list = getSupportedSizeList();
+ for (final Size sz: list) {
+ if ((sz.width == mCurrentWidth)
+ || (sz.height == mCurrentHeight)) {
+ result =sz;
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ @param height
+ */
+ public void setPreviewSize(final int width, final int height) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, mCurrentFrameFormat, mCurrentBandwidthFactor);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ * @param height
+ * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ */
+ public void setPreviewSize(final int width, final int height, final int frameFormat) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, mCurrentBandwidthFactor);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ @param height
+ @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ @param bandwidth [0.0f,1.0f]
+ */
+ public void setPreviewSize(final int width, final int height, final int frameFormat, final float bandwidth) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, bandwidth);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ * @param height
+ * @param min_fps
+ * @param max_fps
+ * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ * @param bandwidthFactor
+ */
+ public void setPreviewSize(final int width, final int height, final int min_fps, final int max_fps, final int frameFormat, final float bandwidthFactor) {
+ if ((width == 0) || (height == 0))
+ throw new IllegalArgumentException("invalid preview size");
+ if (mNativePtr != 0) {
+ final int result = nativeSetPreviewSize(mNativePtr, width, height, min_fps, max_fps, frameFormat, bandwidthFactor);
+ if (result != 0)
+ throw new IllegalArgumentException("Failed to set preview size");
+ mCurrentFrameFormat = frameFormat;
+ mCurrentWidth = width;
+ mCurrentHeight = height;
+ mCurrentBandwidthFactor = bandwidthFactor;
+ }
+ }
+
+ public List getSupportedSizeList() {
+ final int type = (mCurrentFrameFormat > 0) ? 6 : 4;
+ return getSupportedSize(type, mSupportedSize);
+ }
+
+ public List getSupportedSizeList(int frameFormat) {
+ final int type = (frameFormat > 0) ? 6 : 4;
+ return getSupportedSize(type, mSupportedSize);
+ }
+
+ public List getSupportedSize(final int type, final String supportedSize) {
+ final List