Selaa lähdekoodia

implement AudioRecorder

cooperku_kdanmobile 6 vuotta sitten
vanhempi
commit
c14a0e9ea1

+ 182 - 0
src/main/java/com/bomostory/sceneeditmodule/AudioRecorder.kt

@@ -0,0 +1,182 @@
+package com.bomostory.sceneeditmodule
+
+import android.media.AudioFormat
+import android.media.AudioRecord
+import android.media.MediaRecorder
+import android.os.Build
+import com.naman14.androidlame.AndroidLame
+import com.naman14.androidlame.LameBuilder
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.util.concurrent.locks.ReentrantLock
+
+
+class AudioRecorder {
+
+    private var sampleRate = 44100
+    private var recorderChannelConfig = AudioFormat.CHANNEL_IN_MONO
+    private var recorderAudioFormat = AudioFormat.ENCODING_PCM_16BIT
+    private var bufferSize = 1024
+
+    private var audioRecord: AudioRecord? = null
+    private var recordingThread: Thread? = null
+    private var recordingFlag = false
+
+    private var file: File? = null
+    private val lock = ReentrantLock()
+    private var mLame: AndroidLame? = null
+
+    constructor() {
+    }
+
+    public fun setChannelConfig(channelConfig: Int) {
+        this.recorderChannelConfig = channelConfig
+    }
+
+    public fun setAudioFormat(audioFormat: Int) {
+        this.recorderAudioFormat = audioFormat
+    }
+
+    public fun prepare(file: File) {
+        lock.lock()
+        if (this.file == null && audioRecord == null) {
+            this.file = file
+            var parentFile = file.parentFile
+            if (!parentFile.exists() || !parentFile.isDirectory)
+                parentFile.mkdirs()
+            initBufferSizeAndSampleRate()
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                audioRecord = AudioRecord.Builder()
+                    .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
+                    .setAudioFormat(
+                        AudioFormat.Builder()
+                            .setSampleRate(sampleRate)
+                            .setChannelMask(recorderChannelConfig)
+                            .setEncoding(recorderAudioFormat)
+                            .build()
+                    )
+                    .setBufferSizeInBytes(2 * bufferSize)
+                    .build()
+            } else {
+                audioRecord = AudioRecord(
+                    MediaRecorder.AudioSource.VOICE_COMMUNICATION,
+                    sampleRate, recorderChannelConfig, recorderAudioFormat,2 * bufferSize
+                )
+            }
+
+            var builder = LameBuilder()
+                .setInSampleRate(audioRecord!!.sampleRate)
+                .setOutChannels(audioRecord!!.channelCount)
+//                .setOutSampleRate(audioRecord!!.sampleRate)
+            mLame = AndroidLame(builder)
+        }
+        lock.unlock()
+    }
+
+    public fun startRecording() {
+        lock.lock()
+        if (file != null && !isRecording()) {
+            audioRecord?.startRecording()
+            recordingFlag = true
+
+            recordingThread = Thread( { writeAudioDataToFile() }, "AudioRecorder Thread")
+            recordingThread?.start()
+        }
+        lock.unlock()
+    }
+
+    public fun stopRecording() {
+        lock.lock()
+        if (isRecording()) {
+            audioRecord?.stop()
+            recordingFlag = false;
+        }
+        else {
+            release()
+        }
+        lock.unlock()
+    }
+
+    public fun isRecording(): Boolean {
+        return recordingFlag
+    }
+
+    private fun initBufferSizeAndSampleRate() {
+        for (sampleRate in intArrayOf(44100, 22050, 11025, 16000, 8000)) {
+            val bufferSize = AudioRecord.getMinBufferSize(sampleRate, recorderChannelConfig, recorderAudioFormat)
+            if (bufferSize > 0) {
+                this.sampleRate = sampleRate
+                this.bufferSize = bufferSize
+                break
+            }
+        }
+    }
+
+    private fun release() {
+        audioRecord?.release()
+        audioRecord = null
+        recordingThread = null
+        file = null
+
+        mLame?.close()
+        mLame = null
+    }
+
+    private fun writeAudioDataToFile() {
+        val capacity = audioRecord!!.sampleRate * audioRecord!!.channelCount
+        val recordBuffer = ShortArray(capacity)
+        val lameBuffer = ByteArray(capacity)
+
+        var fileOutputStream: FileOutputStream? = null
+
+        try {
+            fileOutputStream = FileOutputStream(file)
+        } catch (e: FileNotFoundException) {
+            e.printStackTrace()
+        }
+
+        if (fileOutputStream != null) {
+            while (isRecording()) {
+                val recorderSample = audioRecord!!.read(recordBuffer, 0, capacity)
+                if (recorderSample > 0) {
+                    val lameSample = mLame!!.encode(recordBuffer, recordBuffer, recorderSample, lameBuffer)
+
+                    if (lameSample > 0) {
+                        try {
+                            fileOutputStream.write(lameBuffer, 0, lameSample)
+                            fileOutputStream.flush()
+                        } catch (e: IOException) {
+                            e.printStackTrace()
+                        }
+                    }
+                }
+            }
+
+            val flushSample = mLame!!.flush(lameBuffer)
+            if (flushSample > 0) {
+                try {
+                    fileOutputStream.write(lameBuffer, 0, flushSample)
+                } catch (e: IOException) {
+                    e.printStackTrace()
+                }
+            }
+
+            try {
+                fileOutputStream.flush()
+            } catch (e: IOException) {
+                e.printStackTrace()
+            }
+
+            try {
+                fileOutputStream.close()
+                fileOutputStream = null
+            } catch (e: IOException) {
+                e.printStackTrace()
+            }
+            release()
+        }
+    }
+}

+ 2 - 0
src/main/java/com/bomostory/sceneeditmodule/Config.kt

@@ -8,9 +8,11 @@ object Config {
     private const val ASSETS_FOLDER_PATH = "/Bomo/Assets"
     private const val PROJECTS_FOLDER_PATH = "/Bomo/Projects"
     private const val IMAGE_FOLDER_PATH = "/Bomo/Image"
+    private const val RECORD_FOLDER_PATH = "/Bomo/Record"
     val IMAGE_FOLDER = File(Environment.getExternalStorageDirectory(), IMAGE_FOLDER_PATH)
     val ASSETS_FOLDER = File(Environment.getExternalStorageDirectory(), ASSETS_FOLDER_PATH)
     val PROJECTS_FOLDER = File(Environment.getExternalStorageDirectory(), PROJECTS_FOLDER_PATH)
+    val RECORD_FOLDER = File(Environment.getExternalStorageDirectory(), RECORD_FOLDER_PATH)
     const val PROJECT_FILE_NAME = "index"
     val PDF_FOLDER = File(ROOT, "pdf")
 }

+ 36 - 21
src/main/java/com/bomostory/sceneeditmodule/SceneEditActivity.kt

@@ -5,45 +5,47 @@ import android.app.ProgressDialog
 import android.content.DialogInterface
 import android.content.Intent
 import android.graphics.Bitmap
+import android.os.Bundle
 import android.support.v7.app.AppCompatActivity
 import android.support.v7.widget.LinearLayoutManager
-import android.os.Bundle
 import android.support.v7.widget.RecyclerView
+import android.util.DisplayMetrics
 import android.view.*
 import android.widget.PopupWindow
+import android.widget.SeekBar
+import com.bomostory.sceneeditmodule.*
+import com.bomostory.sceneeditmodule.basicdata.*
+import com.bomostory.sceneeditmodule.navigationbar.actor.SelectActorView
+import com.bomostory.sceneeditmodule.navigationbar.brush.BrushView
+import com.bomostory.sceneeditmodule.navigationbar.dialogue.DialogueView
+import com.bomostory.sceneeditmodule.screen.movie.MovieEditActivity
+import com.bomostory.sceneeditmodule.screen.view.EditActorView
+import com.bomostory.sceneeditmodule.screen.view.LayerView
+import com.bomostory.sceneeditmodule.screen.view.OnTouchBoMoSceneListener
+import com.bomostory.sceneeditmodule.screen.view.OnTouchSceneListener
+import com.bomostory.sceneeditmodule.utils.FileUtils
+import com.example.tfat.myapplication.navigationbar.EditSceneView
 import com.example.tfat.myapplication.navigationbar.NavigationBarView
+import com.example.tfat.myapplication.navigationbar.RecordFinishView
 import com.example.tfat.myapplication.navigationbar.actor.ActorAdapter
+import com.example.tfat.myapplication.navigationbar.actor.LayerManagementDialog
 import com.example.tfat.myapplication.navigationbar.scene.AddSceneAdapter
 import com.example.tfat.myapplication.navigationbar.scene.AddSceneView
 import com.example.tfat.myapplication.navigationbar.scene.ControlSceneView
 import com.example.tfat.myapplication.navigationbar.scene.SceneAdapter
-import com.bomostory.sceneeditmodule.navigationbar.actor.SelectActorView
 import com.google.gson.Gson
-import kotlinx.android.synthetic.main.activity_scene_edit.*
-import com.bomostory.sceneeditmodule.basicdata.*
-import com.bomostory.sceneeditmodule.navigationbar.dialogue.DialogueView
-import com.example.tfat.myapplication.navigationbar.actor.LayerManagementDialog
-import kotlinx.android.synthetic.main.control_actor_dialog.view.*
-import com.bomostory.sceneeditmodule.utils.FileUtils
-import android.util.DisplayMetrics
-import com.bomostory.sceneeditmodule.EditTextDialog
-import com.bomostory.sceneeditmodule.screen.movie.MovieEditActivity
-import com.example.tfat.myapplication.navigationbar.EditSceneView
-import com.example.tfat.myapplication.navigationbar.RecordFinishView
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.schedulers.Schedulers
+import kotlinx.android.synthetic.main.activity_scene_edit.*
+import kotlinx.android.synthetic.main.control_actor_dialog.view.*
 import kotlinx.android.synthetic.main.layer_management_fragment.*
-import kotlinx.android.synthetic.main.popupview_opacity_dialog.view.*
-import kotlinx.android.synthetic.main.view_control_dialogue_dialog.view.*
-import java.util.concurrent.CopyOnWriteArrayList
-import android.widget.SeekBar
-import com.bomostory.sceneeditmodule.DialogueColorData
-import com.bomostory.sceneeditmodule.SceneDrawer
-import com.bomostory.sceneeditmodule.navigationbar.brush.BrushView
-import com.bomostory.sceneeditmodule.screen.view.*
 import kotlinx.android.synthetic.main.popupview_color_dialog.view.*
+import kotlinx.android.synthetic.main.popupview_opacity_dialog.view.*
 import kotlinx.android.synthetic.main.scene_brush_view.view.*
+import kotlinx.android.synthetic.main.view_control_dialogue_dialog.view.*
+import java.io.File
 import java.util.*
+import java.util.concurrent.CopyOnWriteArrayList
 
 
 class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener, EditActorView.OnActorChangeListener,
@@ -438,9 +440,20 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
         viewContainer.addView(controlSceneView)
         startRecord.visibility = View.VISIBLE
     }
+
+    private fun getRecordFilePath(sceneIndex: Int): String {
+        return "${Config.RECORD_FOLDER}/Project${project.name}_Record_Scene$sceneIndex.mp3"
+    }
+
     private fun initRecord(){
+        val audioRecorder = AudioRecorder()
+        var recordPath = ""
         startRecord.setOnClickListener(View.OnClickListener {
             if (!isRecord) {
+                recordPath = getRecordFilePath(currentSceneIndex)
+                println("recordPath = $recordPath")
+                audioRecorder.prepare(File(recordPath))
+                audioRecorder.startRecording()
                 startRecord.setImageDrawable(resources.getDrawable(R.drawable.ic_btn_record_stop))
                 startRecordTime = System.currentTimeMillis()
                 isRecord = true
@@ -457,6 +470,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
                     }
                 }, 1000, 1000)
             } else {
+                audioRecorder.stopRecording()
                 tv_scene_activity_record_time.visibility = View.INVISIBLE
                 timer.cancel()
                 timer = Timer()
@@ -467,6 +481,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
                 project.story?.let {
                     it.scenes?.let{
                         it[currentSceneIndex].record = record
+                        it[currentSceneIndex].recordPath = recordPath
                     }
                 }
                 startRecord.setImageDrawable(resources.getDrawable(R.drawable.ic_btn_record))

+ 1 - 0
src/main/java/com/bomostory/sceneeditmodule/basicdata/Scene.kt

@@ -12,4 +12,5 @@ class Scene {
     var backgroundPath: String? = null
     var backgroundName: String? = null
     var sceneWidth: Int = 0
+    var recordPath: String? = null
 }