diff --git a/README.md b/README.md index cfd9213..7462e1d 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ abstract class BaseRecorder { * Mute record * 静音录制:录制进行,但是录制的声音是静音的,使用场景是用于和其他音视频进行拼接 */ - open fun muteRecord(mute: Boolean) + open fun muteRecord(mute: Boolean) {/*...*/} //设计背景音乐的url,本地的(网络的可能造成卡死) abstract fun setBackgroundMusic(url: String): BaseRecorder diff --git a/app/src/main/java/me/shetj/mp3recorder/MainActivity.kt b/app/src/main/java/me/shetj/mp3recorder/MainActivity.kt index fcad3a8..3a9161a 100644 --- a/app/src/main/java/me/shetj/mp3recorder/MainActivity.kt +++ b/app/src/main/java/me/shetj/mp3recorder/MainActivity.kt @@ -19,12 +19,12 @@ import kotlinx.coroutines.launch import me.shetj.base.BaseKit import me.shetj.base.fix.FixPermission import me.shetj.base.ktx.setAppearance -import me.shetj.base.ktx.start import me.shetj.base.mvvm.viewbind.BaseBindingActivity import me.shetj.base.mvvm.viewbind.BaseViewModel import me.shetj.mp3recorder.databinding.ActivityMainTestBinding import me.shetj.mp3recorder.record.activity.mix.RecordActivity import me.shetj.mp3recorder.record.utils.AudioManagerX +import me.shetj.recorder.core.AudioUtils class MainActivity : BaseBindingActivity() { private var splashScreen: SplashScreen? =null @@ -67,12 +67,13 @@ class MainActivity : BaseBindingActivity } val audioManagerX = AudioManagerX(this) + val bestSampleRate = AudioUtils.getBestSampleRate(this@MainActivity) mBinding.msg.apply { text = audioManagerX.checkDevice() append("\n\n\n获取当前手机录音最佳参数:") - append("\n最佳采样率:${getBestSampleRate()}\n") - append("\n录音最小缓存大小(${getBestSampleRate()},1,${AudioFormat.ENCODING_PCM_16BIT}):${AudioRecord.getMinBufferSize( - getBestSampleRate(), + append("\n最佳采样率:${bestSampleRate}\n") + append("\n录音最小缓存大小($bestSampleRate,1,${AudioFormat.ENCODING_PCM_16BIT}):${AudioRecord.getMinBufferSize( + bestSampleRate, 1, AudioFormat.ENCODING_PCM_16BIT )}\n") append("音频输出的缓冲:${getBestBufferSize()}\n") @@ -80,15 +81,7 @@ class MainActivity : BaseBindingActivity } - //获取最佳采样率 - private fun getBestSampleRate(): Int { - val am = BaseKit.app.getSystemService(Context.AUDIO_SERVICE) as? AudioManager - val sampleRateStr: String? = am?.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE) - val sampleRate: Int = sampleRateStr?.let { str -> - Integer.parseInt(str).takeUnless { it == 0 } - } ?: 44100 // Use a default value if property not found - return sampleRate - } + // private fun getBestBufferSize(): Int { diff --git a/app/src/main/java/me/shetj/mp3recorder/Recorder.kt b/app/src/main/java/me/shetj/mp3recorder/Recorder.kt index 44f0512..aa66095 100644 --- a/app/src/main/java/me/shetj/mp3recorder/Recorder.kt +++ b/app/src/main/java/me/shetj/mp3recorder/Recorder.kt @@ -18,7 +18,7 @@ fun mp3Recorder( samplingRate: Int = 44100, mp3BitRate: Int = 64,//96(高),32(低) mp3Quality: Int = 1, - @Channel channel: Int = 2, + channel: Int = 2, permissionListener: PermissionListener? = null, recordListener: RecordListener? = null, ): BaseRecorder { diff --git a/app/src/main/java/me/shetj/mp3recorder/record/activity/mix/RecordPage.kt b/app/src/main/java/me/shetj/mp3recorder/record/activity/mix/RecordPage.kt index 6e455f4..4982995 100644 --- a/app/src/main/java/me/shetj/mp3recorder/record/activity/mix/RecordPage.kt +++ b/app/src/main/java/me/shetj/mp3recorder/record/activity/mix/RecordPage.kt @@ -187,7 +187,7 @@ open class RecordPage( override fun onMuteRecordChange(mute: Boolean) { super.onMuteRecordChange(mute) - binding.muteAudio.text = if (mute) "已静音" else "开启禁音" + binding.muteAudio.text = if (mute) "已静音录制" else "开启静音录制" } override fun onMaxChange(time: Long) { diff --git a/app/src/main/java/me/shetj/mp3recorder/record/utils/MediaRecorderKit.kt b/app/src/main/java/me/shetj/mp3recorder/record/utils/MediaRecorderKit.kt index 231d840..0032cd8 100644 --- a/app/src/main/java/me/shetj/mp3recorder/record/utils/MediaRecorderKit.kt +++ b/app/src/main/java/me/shetj/mp3recorder/record/utils/MediaRecorderKit.kt @@ -46,10 +46,6 @@ object MediaRecorderKit { mMediaRecorder!!.setMaxFileSize(10*1024*1024) savePath = EnvironmentStorage.getPath(packagePath = "record") + "/" + System.currentTimeMillis() + ".m4a" mMediaRecorder!!.setOutputFile(savePath) - val nextSavePath = File(EnvironmentStorage.getPath(packagePath = "record") + "/" + System.currentTimeMillis() + ".m4a") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mMediaRecorder!!.setNextOutputFile(nextSavePath) - } /* ③准备 */ mMediaRecorder!!.prepare() /* ④开始 */ @@ -59,6 +55,13 @@ object MediaRecorderKit { } } + fun setNextSavePath(){ + val nextSavePath = File(EnvironmentStorage.getPath(packagePath = "record") + "/" + System.currentTimeMillis() + ".m4a") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mMediaRecorder!!.setNextOutputFile(nextSavePath) + } + } + fun pause(){ mMediaRecorder?.pause() } diff --git a/app/src/main/res/layout/activity_mix_record.xml b/app/src/main/res/layout/activity_mix_record.xml deleted file mode 100644 index c095fa5..0000000 --- a/app/src/main/res/layout/activity_mix_record.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/page_record_mix.xml b/app/src/main/res/layout/page_record_mix.xml index 29381f1..a44218a 100644 --- a/app/src/main/res/layout/page_record_mix.xml +++ b/app/src/main/res/layout/page_record_mix.xml @@ -124,7 +124,7 @@ android:padding="15dp" android:layout_above="@id/rl_record_view" android:background="@drawable/bg_record_add_music" - android:text="开启静音"/> + android:text="开启静音录制"/> + Integer.parseInt(str).takeUnless { it == 0 } + } ?: 44100 // Use a default value if property not found + return sampleRate + } } diff --git a/recorder-core/src/main/java/me/shetj/recorder/core/BaseRecorder.kt b/recorder-core/src/main/java/me/shetj/recorder/core/BaseRecorder.kt index b466af7..2763302 100644 --- a/recorder-core/src/main/java/me/shetj/recorder/core/BaseRecorder.kt +++ b/recorder-core/src/main/java/me/shetj/recorder/core/BaseRecorder.kt @@ -73,7 +73,7 @@ abstract class BaseRecorder { * * * * 48000 一般就够了,太大也会影响文件的大小 */ - protected var mSamplingRate = 44100 + protected var mSamplingRate = 48000 protected var is2Channel = false // 默认是单声道 //设置过滤器 diff --git a/recorder-core/src/main/java/me/shetj/recorder/core/Mp3Option.kt b/recorder-core/src/main/java/me/shetj/recorder/core/Mp3Option.kt index d3a6f77..04eee4c 100644 --- a/recorder-core/src/main/java/me/shetj/recorder/core/Mp3Option.kt +++ b/recorder-core/src/main/java/me/shetj/recorder/core/Mp3Option.kt @@ -13,7 +13,7 @@ data class Mp3Option( // 声音来源:默认麦克风;如果使用VOICE_COMMUNICATION,系统会自动优化录音,但是声音会变小 @Source var audioSource: Int = AudioSource.MIC, // 声道设置:单双声道,1单声道,2双声道 - @Channel var audioChannel: Int = 1, + var audioChannel: Int = 1, // debug,输出录音过程日志 var isDebug: Boolean = false, // 最大录制时间毫秒 @@ -38,6 +38,7 @@ fun recorder(block: Mp3Option.() -> Unit): Mp3Option { return Mp3Option().apply(block) } + /** * * * CAMCORDER 录音来源于同方向的相机麦克风相同,若相机无内置相机或无法识别,则使用预设的麦克风 * * DEFAULT 默认音频源 @@ -63,6 +64,3 @@ fun recorder(block: Mp3Option.() -> Unit): Mp3Option { @Retention(AnnotationRetention.SOURCE) annotation class Source -@IntDef(value = [1, 2]) -@Retention(AnnotationRetention.SOURCE) -annotation class Channel diff --git a/recorder-mix/ReadMe.MD b/recorder-mix/ReadMe.MD index e5f453d..b7e1e33 100644 --- a/recorder-mix/ReadMe.MD +++ b/recorder-mix/ReadMe.MD @@ -1,118 +1,8 @@ [TOC] # MixRecorder -- [MixRecordUtils](https://github.com/SheTieJun/Mp3Recorder/blob/master/app/src/main/java/me/shetj/mp3recorder/record/utils/MixRecordUtils.kt) -### 背景音频要求 - -``` - 44k ,双声道,16位 - 必须保持和录音的参数一样,如果不一样需要自行转码音频或者修改库代码 -``` - -``` - fix : 已经添加参数设置声道数量 - 但是如果设置单声道,然后播放的双声道音乐,音乐会被拉长 -``` - -### 背景音乐实现思路 - -1. 播放音乐,可以得到PCM:[PlayBackMusic](https://github.com/SheTieJun/Mp3Recorder/blob/master/doc/PlayBackMusic.MD) -2. AudioRecord 得到麦克风的声音:PCM -3. 2个PCM进行声音混合,然后Lame转码成MP3 写入到文件 -4. 支持耳机,但是分2种情况,设置了PlugConfig 和没有设置PlugConfig -5. 可以设置释放使用PlugConfig,如果使用将会使用 :没有连接耳机,只用外放的背景音乐;连接耳机,会使用写入合成背景音乐的方式 - -#### 1. 初始化 - - -```kotlin -recorder { - recordListener = this@MixRecordUtils - permissionListener = this@MixRecordUtils - isDebug = false - pcmListener = this@RecordUtils -}.buildMix(context) -``` - -#### 2. 开始录音 - -```kotlin - mixRecorder!!.start() -``` - -#### 3. 暂停、重新开始录音 - -```kotlin -mRecorder?.onPause() //暂停 -mRecorder?.onResume() //重新开始 -mRecorder?.state //当前录音的状态 3个专题,停止,录音中,暂停 -``` - -#### 4. 背景音乐相关 - -```kotlin -mRecorder?.setBackgroundMusic(musicUrl)//设置背景音乐 -mRecorder?.setVolume(volume)//设置背景音乐大小0-1,即使没有把手机的声音关闭,也会按照该比例记录到录制的声音中 -mRecorder?.startPlayMusic() //开始播放背景音乐 -mRecorder?.isPlayMusic() //是否播放了背景应用 -mRecorder?.pauseMusic() //暂停背景音乐 -mRecorder?.isPauseMusic()// 背景音乐是否暂停 -mRecorder?.resumeMusic() //重新开始播放 -``` -#### 5.中途替换输出文件 -``` -//filePath 新的文件 -mRecorder.updateDataEncode(filePath) -``` - -#### 6.中途暂停、重置 -``` -mRecorder.onReset() //会回调 Listener: onReset() -``` -或者 -``` -mRecorder.onDestroy() //没有任何回调 -``` - -#### 6. 停止录音 - -```kotlin -mRecorder?.stop() //完成录音 -``` - -#### 7. 强制启动写入的混音 - - -#### 单双声道 - -Lame 在使用lame_encode_buffer 在转码双声道时 会出现噪音 - -解决方法 -``` - if (is2CHANNEL) { - readSize = buffer.size / 2 - encodedSize = LameUtils.encodeInterleaved(buffer,readSize,mMp3Buffer) - } else { - readSize = buffer.size - encodedSize = LameUtils.encode(buffer, buffer, readSize, mMp3Buffer) - } -``` - -[音频基础知识](https://blog.csdn.net/StjunF/article/details/121296111) - -``` - /* - * 16Kbps= 电话音质 - * 24Kbps= 增加电话音质、短波广播、长波广播、欧洲制式中波广播 - * 40Kbps= 美国制式中波广播 - * 56Kbps= 话音 - * 64Kbps= 增加话音(手机铃声最佳比特率设定值、手机单声道MP3播放器最佳设定值) - * 112Kbps= FM调频立体声广播 - * 128Kbps= 磁带(手机立体声MP3播放器最佳设定值、低档MP3播放器最佳设定值) - * 160Kbps= HIFI高保真(中高档MP3播放器最佳设定值) - * 192Kbps= CD(高档MP3播放器最佳设定值) - * 256Kbps= Studio音乐工作室(音乐发烧友适用) - * 实际上随着技术的进步,比特率也越来越高,MP3的最高比特率为320Kbps,但一些格式可以达到更高的比特率和更高的音质。 - * 比如正逐渐兴起的APE音频格式,能够提供真正发烧级的无损音质和相对于WAV格式更小的体积,其比特率通常为550kbps-----950kbps。 - */ -``` +- 边录边转码MP3,默认启动系统自带[如果手机支持]的AEC、NC、AGC,可以通过`enableAudioEffect`进行开启和关闭,除1.9.1是默认关闭的(因为部分手机外接麦克风时,如果是【MIC+AEC、NC、AGC】,会没有声音),其他版本都是默认开启。 +- 支持暂停,实时返回已**录制时长**和当前**声音大小**,已录制的那段音频是**可以播放**的 +- 支持添加背景音乐,可以设置背景音乐声音的大小(但是不建议设置,因为通过算法声音的会失真) +- 支持静音录制 +- 支持录制时间返回,【1.9.2 已优化误差,在这之前录制越长误差越大】 \ No newline at end of file diff --git a/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/MixRecorder.kt b/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/MixRecorder.kt index 6b4b1ea..e8e72e1 100644 --- a/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/MixRecorder.kt +++ b/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/MixRecorder.kt @@ -12,7 +12,6 @@ import android.util.Log import me.shetj.ndk.lame.LameUtils import me.shetj.player.PlayerListener import me.shetj.recorder.core.BaseRecorder -import me.shetj.recorder.core.Channel import me.shetj.recorder.core.PlugConfigs import me.shetj.recorder.core.RecordState import me.shetj.recorder.core.VolumeConfig @@ -89,7 +88,7 @@ internal class MixRecorder : BaseRecorder { bgPlayer.updateChannel(mLameInChannel) } - override fun setAudioChannel(@Channel channel: Int): Boolean { + override fun setAudioChannel(channel: Int): Boolean { if (!isActive) { is2Channel = channel == 2 mLameInChannel = channel @@ -418,7 +417,7 @@ internal class MixRecorder : BaseRecorder { mEncodeThread!!.setPCMListener(mPCMListener) mAudioRecord!!.setRecordPositionUpdateListener(mEncodeThread, mEncodeThread!!.getEncodeHandler()) mAudioRecord!!.positionNotificationPeriod = FRAME_COUNT - + bgPlayer.mSampleRate = mSamplingRate // 强制加上背景音乐 plugConfigs?.setForce(enableForceMix || (mAudioSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION)) } @@ -431,7 +430,7 @@ internal class MixRecorder : BaseRecorder { */ override fun enableForceWriteMixBg(enable: Boolean) { this.enableForceMix = enable - plugConfigs?.setForce(enable) + this.plugConfigs?.setForce(enable) } //region private method 私有方法 diff --git a/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/PlayBackMusic.kt b/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/PlayBackMusic.kt index 60ddc6e..31b5d35 100644 --- a/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/PlayBackMusic.kt +++ b/recorder-mix/src/main/java/me/shetj/recorder/mixRecorder/PlayBackMusic.kt @@ -21,7 +21,6 @@ import java.util.concurrent.LinkedBlockingDeque import java.util.concurrent.atomic.AtomicBoolean import me.shetj.player.PlayerListener import me.shetj.recorder.core.BaseRecorder -import me.shetj.recorder.core.Channel import me.shetj.recorder.core.PlugConfigs /** @@ -36,7 +35,7 @@ import me.shetj.recorder.core.PlugConfigs * TODO seekTo 缺失功能 * */ -internal class PlayBackMusic(private var defaultChannel: Int = CHANNEL_OUT_MONO, var plugConfigs: PlugConfigs?) { +internal class PlayBackMusic(private var defaultChannel: Int = CHANNEL_OUT_MONO,var plugConfigs: PlugConfigs?) { var isPlayingMusic = false private set @@ -48,7 +47,7 @@ internal class PlayBackMusic(private var defaultChannel: Int = CHANNEL_OUT_MONO, } else { mAudioDecoder!!.bufferSize } - + var mSampleRate :Int= 44100 private var mAudioDecoder: AudioDecoder? = null private val backGroundBytes = LinkedBlockingDeque() // new ArrayDeque<>();// ArrayDeque不是线程安全的 private var mIsRecording = false // 路由中 @@ -405,7 +404,7 @@ internal class PlayBackMusic(private var defaultChannel: Int = CHANNEL_OUT_MONO, /** * 更新声道数量 */ - internal fun updateChannel(@Channel channel: Int) { + internal fun updateChannel(channel: Int) { defaultChannel = when (channel) { 1 -> { CHANNEL_OUT_MONO @@ -428,7 +427,6 @@ internal class PlayBackMusic(private var defaultChannel: Int = CHANNEL_OUT_MONO, private const val PROCESS_REPLAY = PROCESS_ERROR + 1 private const val PROCESS_PLAYING = PROCESS_REPLAY + 1 private const val PROCESS_COMPLETE = PROCESS_PLAYING + 1 - private const val mSampleRate = 44100 private const val mAudioEncoding = AudioFormat.ENCODING_PCM_16BIT // 一个采样点16比特-2个字节 } } diff --git a/recorder-sim/README.MD b/recorder-sim/README.MD index 4f14a9d..fe41440 100644 --- a/recorder-sim/README.MD +++ b/recorder-sim/README.MD @@ -1 +1,2 @@ ## [使用说明文档](https://github.com/SheTieJun/Mp3Recorder/wiki/%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) +简易版,这个一开始版本,所以有部分功能没有同步 \ No newline at end of file diff --git a/recorder-sim/src/main/AndroidManifest.xml b/recorder-sim/src/main/AndroidManifest.xml index a8510eb..0af680d 100644 --- a/recorder-sim/src/main/AndroidManifest.xml +++ b/recorder-sim/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/recorder-sim/src/main/java/me/shetj/recorder/simRecorder/SimRecorder.kt b/recorder-sim/src/main/java/me/shetj/recorder/simRecorder/SimRecorder.kt index 0565dd1..fc452f1 100644 --- a/recorder-sim/src/main/java/me/shetj/recorder/simRecorder/SimRecorder.kt +++ b/recorder-sim/src/main/java/me/shetj/recorder/simRecorder/SimRecorder.kt @@ -1,5 +1,6 @@ package me.shetj.recorder.simRecorder +import android.annotation.SuppressLint import android.content.Context import android.media.AudioFormat import android.media.AudioRecord @@ -13,7 +14,6 @@ import me.shetj.ndk.lame.LameUtils import me.shetj.player.AudioPlayer import me.shetj.player.PlayerListener import me.shetj.recorder.core.BaseRecorder -import me.shetj.recorder.core.Channel import me.shetj.recorder.core.RecordState import me.shetj.recorder.core.Source import me.shetj.recorder.core.VolumeConfig @@ -37,18 +37,6 @@ internal class SimRecorder : BaseRecorder { // 缓冲数量 private var mBufferSize: Int = 0 - /** - * pcm数据的速度,默认300 - * 数据越大,速度越慢 - */ - private var waveSpeed = 500 - set(waveSpeed) { - if (this.waveSpeed <= 0) { - return - } - field = waveSpeed - } - // 背景音乐相关 private var backgroundMusicUrl: String? = null private var backgroundMusicPlayerListener: PlayerListener? = null @@ -84,7 +72,7 @@ internal class SimRecorder : BaseRecorder { * * @param audioSource MediaRecorder.AudioSource.MIC */ - constructor(@Source audioSource: Int = MediaRecorder.AudioSource.MIC, @Channel channel: Int = 1) { + constructor(@Source audioSource: Int = MediaRecorder.AudioSource.MIC, channel: Int = 1) { this.mAudioSource = audioSource this.mLameInChannel = channel this.mChannelConfig = when (channel) { @@ -102,7 +90,7 @@ internal class SimRecorder : BaseRecorder { releaseAEC() } - override fun setAudioChannel(@Channel channel: Int): Boolean { + override fun setAudioChannel(channel: Int): Boolean { if (isActive) { Log.e(TAG, "setAudioChannel error ,need state isn't isActive|录音没有完成,无法进行修改 ") return false @@ -360,6 +348,7 @@ internal class SimRecorder : BaseRecorder { /** * Initialize audio recorder */ + @SuppressLint("MissingPermission") @Throws(IOException::class) private fun initAudioRecorder() { mBufferSize = AudioRecord.getMinBufferSize( diff --git a/recorder-st/src/main/AndroidManifest.xml b/recorder-st/src/main/AndroidManifest.xml index ed496ba..89fa83c 100644 --- a/recorder-st/src/main/AndroidManifest.xml +++ b/recorder-st/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/recorder-st/src/main/java/me/shetj/recorder/soundtouch/STRecorder.kt b/recorder-st/src/main/java/me/shetj/recorder/soundtouch/STRecorder.kt index 99b8fad..0df5cdd 100644 --- a/recorder-st/src/main/java/me/shetj/recorder/soundtouch/STRecorder.kt +++ b/recorder-st/src/main/java/me/shetj/recorder/soundtouch/STRecorder.kt @@ -13,7 +13,6 @@ import java.io.IOException import me.shetj.ndk.lame.LameUtils import me.shetj.player.PlayerListener import me.shetj.recorder.core.BaseRecorder -import me.shetj.recorder.core.Channel import me.shetj.recorder.core.ISoundTouchCore import me.shetj.recorder.core.RecordState import me.shetj.recorder.core.Source @@ -67,7 +66,7 @@ internal class STRecorder : BaseRecorder { */ constructor( @Source audioSource: Int = MediaRecorder.AudioSource.MIC, - @Channel channel: Int = 1 + channel: Int = 1 ) { this.mAudioSource = audioSource mLameInChannel = channel @@ -90,7 +89,7 @@ internal class STRecorder : BaseRecorder { return soundTouch } - override fun setAudioChannel(@Channel channel: Int): Boolean { + override fun setAudioChannel(channel: Int): Boolean { if (isActive) { Log.e(TAG, "setAudioSource error ,need state isn't isActive|录音没有完成,无法进行修改 ") return false