Ver Fonte

Merge branch 'record'

cooperku_kdanmobile há 6 anos atrás
pai
commit
a7b3f44427

+ 1 - 0
build.gradle

@@ -62,6 +62,7 @@ dependencies {
     implementation project(':pdfexport')
 
     implementation 'com.squareup.picasso:picasso:2.71828'
+    implementation 'com.github.naman14:TAndroidLame:1.1'
 }
 repositories {
     mavenCentral()

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

@@ -0,0 +1,198 @@
+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 cancelRecording() {
+        lock.lock()
+        var filename = file?.absolutePath
+        if (isRecording()) {
+            audioRecord?.stop()
+            recordingFlag = false;
+        }
+        else {
+            release()
+        }
+        if (filename != null) {
+            File(filename).delete()
+        }
+        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")
 

+ 65 - 19
src/main/java/com/bomostory/sceneeditmodule/SceneEditActivity.kt

@@ -5,52 +5,55 @@ 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 android.util.Log
 import android.widget.RadioGroup
 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.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.actor.ObjectView
-import com.bomostory.sceneeditmodule.navigationbar.brush.BrushView
-import com.bomostory.sceneeditmodule.screen.view.*
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
 import kotlinx.android.synthetic.main.actor_select_view.view.*
-import kotlinx.android.synthetic.main.movie_item_view.*
+import kotlinx.android.synthetic.main.layer_management_fragment.*
 import kotlinx.android.synthetic.main.navigation_bar_view.view.*
 import kotlinx.android.synthetic.main.popupview_color_dialog.view.*
 import kotlinx.android.synthetic.main.popupview_setting.view.*
 import kotlinx.android.synthetic.main.scene_brush_view.view.*
+import java.io.File
 import java.util.*
+import java.util.concurrent.CopyOnWriteArrayList
 
 
 class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener, EditActorView.OnActorChangeListener,
@@ -75,6 +78,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
     private var editTextDialog = EditTextDialog()
     private val monitorSize = DisplayMetrics()
     private var timer = Timer()
+    private val audioRecorder = AudioRecorder()
     companion object {
         const val PHOTO_FROM_GALLERY = 1
         const val LAYER_MANAGEMENT = "layer_management"
@@ -492,9 +496,48 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
         viewContainer.addView(controlSceneView)
         startRecord.visibility = View.VISIBLE
     }
+
+    override fun onPause() {
+        super.onPause()
+        cancelRecording()
+    }
+
+    private fun cancelRecording() {
+        audioRecorder.cancelRecording()
+
+        tv_scene_activity_record_time.visibility = View.INVISIBLE
+        timer.cancel()
+        timer = Timer()
+        recordingTime = 0L
+        tv_scene_activity_record_time.text = "Recording 00:00"
+        isRecord = false
+        startRecord.setImageDrawable(resources.getDrawable(R.drawable.ic_btn_record))
+        navigationBar.visibility = View.VISIBLE
+        initControlBarView()
+        initControlSceneView()
+        initRecordFinishView()
+    }
+
+    private fun getRecordFilePath(): String {
+        return "${Config.RECORD_FOLDER}/Project${project.name}_Record_Scene${System.currentTimeMillis()}.mp3"
+    }
+
     private fun initRecord(){
+        var recordPath: String? = null
         startRecord.setOnClickListener(View.OnClickListener {
             if (!isRecord) {
+                //  delete record
+                project.story?.let {
+                    it.scenes?.let{
+                        it[currentSceneIndex].record = null
+                        if (it[currentSceneIndex].recordPath != null)
+                            File(it[currentSceneIndex].recordPath).delete()
+                    }
+                }
+                recordPath = getRecordFilePath()
+                println("recordPath = $recordPath")
+                audioRecorder.prepare(File(recordPath))
+                audioRecorder.startRecording()
                 startRecord.setImageDrawable(resources.getDrawable(R.drawable.ic_btn_record_stop))
                 startRecordTime = System.currentTimeMillis()
                 isRecord = true
@@ -511,6 +554,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
                     }
                 }, 1000, 1000)
             } else {
+                audioRecorder.stopRecording()
                 tv_scene_activity_record_time.visibility = View.INVISIBLE
                 timer.cancel()
                 timer = Timer()
@@ -522,6 +566,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
                     project.story?.let {
                         it.scenes?.let{
                             it[currentSceneIndex].record = record
+                            it[currentSceneIndex].recordPath = recordPath
                         }
                     }
                 } else {
@@ -547,6 +592,7 @@ class SceneEditActivity : AppCompatActivity(), ActorAdapter.OnActorDragListener,
                     project.story?.let {
                         it.scenes?.let{
                             it[currentSceneIndex].record = autoSwipeRecord
+                            it[currentSceneIndex].recordPath = recordPath
                         }
                     }
                 }

+ 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
 }