├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── drawable-v24 │ │ │ │ ├── draw_picture_ic_flowers.png │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── bacchus │ │ │ └── audiovideo │ │ │ ├── draw_picture │ │ │ ├── CustomView.kt │ │ │ └── MainActivity.kt │ │ │ └── audio_record │ │ │ └── AudioDemo.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── bacchus │ │ │ └── audiovideo │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── bacchus │ │ └── audiovideo │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── notelib ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── com │ │ └── bacchus │ │ └── notelib │ │ ├── Note.java │ │ ├── image │ │ ├── imooc_cdn.png │ │ ├── imooc_tone.png │ │ ├── cnblog_pull.png │ │ ├── cnblog_push.png │ │ ├── imooc_masking.png │ │ ├── imooc_process.png │ │ ├── imooc_realtime.png │ │ ├── imooc_soundwave.png │ │ ├── imooc_quantificat.png │ │ └── imooc_entertainment.png │ │ ├── imooc │ │ ├── 音频入门.md │ │ └── 万人直播架构与CDN网络.md │ │ └── cnblog │ │ └── Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记).md └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── misc.xml ├── runConfigurations.xml └── gradle.xml ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /notelib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':notelib' 2 | rootProject.name='AudioVideo' 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AudioVideo 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/Note.java: -------------------------------------------------------------------------------- 1 | package com.bacchus.notelib; 2 | 3 | public class Note { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/draw_picture_ic_flowers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/app/src/main/res/drawable-v24/draw_picture_ic_flowers.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_cdn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_cdn.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_tone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_tone.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/cnblog_pull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/cnblog_pull.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/cnblog_push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/cnblog_push.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_masking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_masking.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_process.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_realtime.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_soundwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_soundwave.png -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_quantificat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_quantificat.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/image/imooc_entertainment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bacchuc/AudioVideo/HEAD/notelib/src/main/java/com/bacchus/notelib/image/imooc_entertainment.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /notelib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | dependencies { 4 | implementation fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | sourceCompatibility = "7" 8 | targetCompatibility = "7" 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 20 15:53:03 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/bacchus/audiovideo/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.bacchus.audiovideo 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bacchus/audiovideo/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.bacchus.audiovideo 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.bacchus.audiovideo", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/bacchus/audiovideo/draw_picture/CustomView.kt: -------------------------------------------------------------------------------- 1 | package com.bacchus.audiovideo.draw_picture 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.BitmapFactory 6 | import android.graphics.Canvas 7 | import android.graphics.Paint 8 | import android.view.View 9 | import com.bacchus.audiovideo.R 10 | 11 | class CustomView(context: Context?) : View (context){ 12 | 13 | private val paint = Paint() 14 | var bitmap: Bitmap 15 | 16 | init { 17 | paint.isAntiAlias = true 18 | paint.style = Paint.Style.STROKE 19 | bitmap = BitmapFactory.decodeResource(resources, R.drawable.draw_picture_ic_flowers) 20 | } 21 | 22 | override fun onDraw(canvas: Canvas?) { 23 | super.onDraw(canvas) 24 | canvas?.drawBitmap(bitmap, 0f, 0f, paint) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 20 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "com.bacchus.audiovideo" 11 | minSdkVersion 18 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 28 | implementation 'androidx.appcompat:appcompat:1.0.2' 29 | implementation 'androidx.core:core-ktx:1.0.2' 30 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 34 | } 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioVideo: 音视频资料整理、笔记、Demo By Kotlin 2 | **欢迎 Star 和帮忙改进, 有任何意见或建议,到这里提出 [Create New Issue](https://github.com/Bacchuc/AudioVideo/issues/new)** 3 | 4 | ## 个人笔记 5 | 6 | #### 音频基础 7 | - [音频入门](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/imooc/%E9%9F%B3%E9%A2%91%E5%85%A5%E9%97%A8.md) 8 | - [万人直播架构与CDN网络](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/imooc/%E4%B8%87%E4%BA%BA%E7%9B%B4%E6%92%AD%E6%9E%B6%E6%9E%84%E4%B8%8ECDN%E7%BD%91%E7%BB%9C.md) 9 | - [使用 AudioRecord 采集音频 PCM 并保存到文件](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/cnblog/Android%20%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91(%E4%BA%8C)%EF%BC%9A%E4%BD%BF%E7%94%A8%20AudioRecord%20%E9%87%87%E9%9B%86%E9%9F%B3%E9%A2%91PCM%E5%B9%B6%E4%BF%9D%E5%AD%98%E5%88%B0%E6%96%87%E4%BB%B6%EF%BC%88%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%89.md) 10 | 11 | 12 | ## 资源文章 13 | - [Jhuster的专栏](https://blog.51cto.com/ticktick/category15.html) 14 | - [雷霄骅的专栏](https://blog.csdn.net/leixiaohua1020) 15 | - [Android 音视频开发学习思路 - 灰色飘零 - 博客园](https://www.cnblogs.com/renhui/p/7452572.html) 16 | - [Home - Sharry Blog](https://sharrychoo.github.io/blog/) 17 | - [LearnOpenGL-CN](https://learnopengl-cn.readthedocs.io/zh/latest/) 18 | - [Android Graphic 架构](https://source.android.com/devices/graphics/) 19 | 20 | ## 联系方式 21 | - [Blog](https://blog.csdn.net/qq_35699070) 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/bacchus/audiovideo/audio_record/AudioDemo.kt: -------------------------------------------------------------------------------- 1 | package com.bacchus.audiovideo.audio_record 2 | 3 | import android.media.AudioRecord 4 | import android.util.Log 5 | 6 | /** 7 | * 使用 AudioRecord 实现录音,并生成 wav 8 | * @time : 2019/08/20 9 | * @author : Bacchus 10 | */ 11 | class AudioDemo private constructor() { 12 | 13 | companion object { 14 | val instance: AudioDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { 15 | AudioDemo() 16 | } 17 | } 18 | 19 | // 缓冲区字节大小 20 | private var mBufferSizeInBytes = 0 21 | 22 | // AudioRecord 录音对象 23 | private lateinit var mAudioRecord: AudioRecord 24 | 25 | init { 26 | createDefaultAudio() 27 | } 28 | 29 | /** 30 | * 创建默认的录音对象 31 | */ 32 | private fun createDefaultAudio() { 33 | // 获得缓冲区字节大小,AudioRecord 能接受的最小的 buffer 大小 34 | mBufferSizeInBytes = AudioRecord.getMinBufferSize( 35 | Constants.AUDIO_SAMPLE_RATE, 36 | Constants.AUDIO_CHANNEL, 37 | Constants.AUDIO_ENCODING 38 | ) 39 | mAudioRecord = AudioRecord( 40 | Constants.AUDIO_INPUT, 41 | Constants.AUDIO_SAMPLE_RATE, 42 | Constants.AUDIO_CHANNEL, 43 | Constants.AUDIO_ENCODING, 44 | mBufferSizeInBytes 45 | ) 46 | } 47 | 48 | fun startRecord() { 49 | Log.i("startRecord", "startRecord") 50 | 51 | } 52 | 53 | fun stopRecord() { 54 | Log.i("stopRecord", "stopRecord") 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bacchus/audiovideo/draw_picture/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.bacchus.audiovideo.draw_picture 2 | 3 | import android.graphics.BitmapFactory 4 | import android.graphics.Canvas 5 | import android.graphics.Paint 6 | import androidx.appcompat.app.AppCompatActivity 7 | import android.os.Bundle 8 | import android.util.TypedValue 9 | import android.view.SurfaceHolder 10 | import kotlinx.android.synthetic.main.activity_main.* 11 | import android.graphics.Bitmap 12 | import android.graphics.drawable.Drawable 13 | import android.os.Build 14 | import com.bacchus.audiovideo.R 15 | 16 | class MainActivity : AppCompatActivity() { 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContentView(R.layout.activity_main) 21 | setImageView() 22 | setSurfaceView() 23 | } 24 | 25 | fun setImageView() { 26 | iv.setImageBitmap(BitmapFactory.decodeResource(resources, R.drawable.draw_picture_ic_flowers)) 27 | } 28 | 29 | fun setSurfaceView() { 30 | sv.holder.addCallback(object : SurfaceHolder.Callback { 31 | override fun surfaceCreated(surfaceHolder: SurfaceHolder) { 32 | val paint = Paint() 33 | paint.isAntiAlias = true 34 | paint.style = Paint.Style.STROKE 35 | 36 | val canvas = surfaceHolder.lockCanvas() 37 | canvas.drawBitmap(BitmapFactory.decodeResource(resources, R.drawable.draw_picture_ic_flowers), 0f, 0f, paint) 38 | 39 | surfaceHolder.unlockCanvasAndPost(canvas) 40 | } 41 | 42 | override fun surfaceChanged(surfaceHolder: SurfaceHolder, i: Int, i1: Int, i2: Int) { 43 | 44 | } 45 | 46 | override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) { 47 | 48 | } 49 | }) 50 | } 51 | 52 | fun setCustomView(){ 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/imooc/音频入门.md: -------------------------------------------------------------------------------- 1 | #### 声音三要素 2 | - 音调:就是音频,男生 -> 女生 -> 儿童。声音越高,音频越快 3 | - 音量:振动的幅度,幅度越大,声音越大 4 | - 音色:它与材质有很大关系,本质是谐波 5 | 6 | ![](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_soundwave.png) 7 | 8 | 声音在说话时会产生一个正弦波,同时会存在一些小的谐波,但是正弦波是比较理想的情况下,通常会伴随着一些畸形波,畸形波一般来说就是噪音, 9 | 越接近正弦,声音越好听 10 | 11 | ![音色与音品](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_tone.png) 12 | 13 | #### 人类听觉范围 14 | - 次声波 -> (20 Hz) 可听声波 (20 KHz) -> 超声波 15 | 所以取音频时,我们只取可听声波的赫兹范围内的声波,可以减少数据存储 16 | 17 | #### 量化基本概念 18 | - 采样大小:(即下图的振幅的量化高度的值)一个采样用多少 bit 存放,常用的是16 bit。声音的振幅基本上16够用了,声音没有负值。 19 | - 采样率:采样频率 8k(一秒采集8千次,此处跟音频无关,但是可以通过音频大小与采样率计算出一个正弦波中的采样次数)、16k、32k、44.1k、48k, 20 | - 声道数:单声道、双声道、多声道(喇叭数量) 21 | - 音频帧:音频跟视频很不一样,视频每一帧就是一张图像,而从下面的正玄波可以看出,音频数据是流式的,本身没有明确的一帧帧的概念,在实际的应用中,为了音频算法处理/传输的方便,一般约定俗成取2.5ms~60ms为单位的数据量为一帧音频。这个时间被称之为“采样时间”,其长度没有特别的标准,它是根据具体应用的需求来决定的 22 | 23 | ![音频量化过程](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_quantificat.png) 24 | 25 | #### 计算 26 | - 码率计算: 采样率 * 采样大小 * 声道数 27 | 28 | ``` 29 | 例如: 采样率为 44.1 KHz 30 | 采样大小为 16 bit 31 | 双声道的 PCM(音频流) 编码的 WAV 文件 32 | 它的率码为 44.1K * 16 * 2 = 1411.2kb/s 33 | ``` 34 | 35 | - 一帧音频帧的大小: 采样率 * 采样大小 * 音频帧 * 声道数 36 | 37 | ``` 38 | 例如: 采样率为 8 KHz 39 | 采样大小为 16 bit 40 | 20ms一帧 41 | 双声道 42 | 它的大小为 8000 * 16 * 0.02 * 2 = 5120 bit = 640 byte 43 | ``` 44 | 45 | #### 音频压缩技术 46 | - 消除冗余数据,有损压缩(直接删除不能复原) 47 | - 哈夫曼无损编码,无损压缩(将冗余数据进行编码后可以进行复原) 48 | - 音频冗余信息 49 | - 压缩的主要方法是去除采集到的音频冗余信息,所谓冗余信息包括 50 | 人耳听觉范围外的音频信号以及被掩蔽掉的音频信号 51 | - 信号的掩蔽可以分为 **频域掩蔽** 和 **时域掩蔽** 52 | 53 | ![频域掩蔽效应](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_masking.png) 54 | 55 | ``` 56 | 频谱掩蔽效应: 人耳所能察觉的声音信号的频率范围为20Hz~20KHz,在这个频率范围以外的音频信号属于冗余信号 57 | 一般来说,弱纯音离强纯音越近就越容易被掩蔽;低频纯音可以有效地掩蔽高频纯音,但高频纯音对 58 | 低频纯音的掩蔽作用则不明显 59 | 60 | 时域掩蔽效应: 除了同时发出的声音之间有掩蔽现象之外,在时间上相邻的声音之间也有掩蔽现象,并且称为时域掩蔽 61 | 时域掩蔽又分为超前掩蔽(pre-masking)和滞后掩蔽(post-masking),产生时域掩蔽的主要原因是 62 | 人的大脑处理信息需要花费一定的时间。一般来说,超前掩蔽很短,只有大约5~20ms,而滞后掩蔽 63 | 可以持续50~200ms。当强音信号和弱音信号同时出现时,弱信号会听不到,因此,弱音信号也属于 64 | 冗余信号。 65 | ``` 66 | #### 音频编码过程 67 | 68 | ![编码过程](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_process.png) 69 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/imooc/万人直播架构与CDN网络.md: -------------------------------------------------------------------------------- 1 | ## CDN网络介绍 2 | 3 | #### 泛娱乐化直播架构 4 | - 共享端(美女直播端:电脑、手机)发起信令到信令服务器,信令服务器进行逻辑处理(例如:创建房间、聊天、礼物都是以信令的方式进行传输处理的), 5 | - 流媒体云( CDN ):对音视频流( RTMP )进行转发,将信号转发给收看用户 6 | ![泛娱乐化直播架构](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_entertainment.png) 7 | 8 | 流程描述 9 | - 共享端 10 | - 共享端发送信令到信令服务器,请求创建房间 11 | - 信令服务器收到信令后,创建房间,然后给共享端返回一个流媒体云的地址 12 | - 共享端采集自己的音视频数据,形成 RTMP 流上传到流媒体云网络 13 | - 用户收看端 14 | - 用户发送请求加入房间的信令给信令服务器 15 | - 信令服务器将此用户加入到房间,然后返回此用户请求的该美女主播房间的 CDN 流地址 16 | - 用户通过此地址拉取此流从而观看该节目 17 | 18 | 可能会有很多架构变形,但是基本上万变不离其宗,大致都是这个样子 19 | 20 | #### 实时互动直播架构 21 | - TCP 协议:通过确认、超时、重发来使包的传输可以准确无误,但是也使其无法更好的进行实时传输,所以实时互动直播架构采用的是 UDP 协议,使用 UDP 协议就需要用到自有网络,不能只使用 CDN 网络了 22 | - 音视频数据有时效性,所以允许丢包,但是丢包了就会影响质量 23 | - 有多个服务节点,一旦某个节点有问题就会转移到其余节点,对于用户来说是无感知的 24 | ,也能保证负载均衡 25 | - 由于有多个节点,所以有一个控制中心,控制中心与节点之间是通过心跳包来联系的,隔一段时间,节点都会向控制中心发送此刻的:CPU 占用情况,IO 占用情况、网络占用情况等等 26 | - 控制中心会根据这些信息来调拨这些节点,例如将数据交给 CPU 占用较低的节点处理 27 | - 心跳包是通过内总线进行传输的,内总线的容量更高 28 | - 以上就为实时互动架构,而通过媒体服务器进行转换给 CDN 网络,即将 UDP 协议转化为 RTMP 协议 29 | ![实时互动直播架构](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_realtime.png) 30 | 31 | #### CDN 网络 32 | - CDN 网络是为了解决用户访问网络资源慢而出现的技术 33 | - 网络资源慢的原因 34 | - 数据链路长:海南访问北京的用户的数据,数据传输过程中还可能有节点会出现一些错误情况,如果将北京的服务直接搬到海南的服务中,就可以直接访问海南的服务了 35 | - 运营商之间为了利益不允许其他运营商的网络访问自己的网络,例如联通的不让访问电信的 36 | - CDN 构成 37 | - 边缘节点:用户从边缘节点(指的是网络,不是物理距离)上获取数据 38 | - 二级节点:二级节点就是主干网节点,主要用于缓存大量数据,减轻源站压力。例如当边缘节点有数据时会直接返回给用户,无数据时,会向上访问主干节点,将数据拉取到自己的边缘节点再传输给用户, 39 | - 源站:CP (内容提供方)将内容放到源站,当主干节点也没数据时就会到源站节点获取数据 40 | - 有一些大型公司可能会因为数据很大,会分为更多层节点,但是大体上是这么一个结构 41 | - CDN 网络 42 | - 在各大运营商中间设立主打节点,通过光纤将两个节点打通,这样就可以解决不同运营商之间的数据传输问题 43 | - 用户访问流程 44 | - 用户访问时会先访问 CDN 网络,然后 CDN 网络会判断该用户是什么运营商,然后找到最近的边缘节点,若边缘节点无数据,则向上请求主干节点,若主干节点依然没有数据则继续请求源站 45 | - 每次请求到达每一个节点,若是第一次,则会进行缓存,酱紫下一次请求或者其他用户请求时速度就会加快 46 | - CDN 网络的两种类型 47 | - 传统网络:热点型,若请求多次的会存在主干中,超时后删除 48 | - 直播类型:对延时性要求比较高,所以源站会一次性将所有数据 Push 到主干节点中,边缘节点直接从主干节点中进行拉取 49 | ![CDN 网络](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/imooc_cdn.png) 50 | 51 | #### 工具 52 | - FFmpeg: 53 | - 只需要一个命令就可以将一个视频格式转换为其他视频格式 54 | - 音视频的抽取 55 | - 视频上打水印 56 | - FFPlay 57 | - 使用命令行,不是图形化,是基于 FFmpeg 进行二次化开发的工具 58 | - FlashPlayer 59 | - 用于对 RTMP 协议进行分析 60 | 61 | #### 搭建流媒体服务 62 | - 准备流媒体服务器(商业最好是 Linux,不建议用 Windows,所以平时用 Mac) 63 | - 编译并安装 Nginx 服务( Web 服务器) 64 | - 配置 RTMP 服务并启动 Nginx 服务 65 | 66 | #### 安装配置步骤 67 | - 到 iTerm 中输入:brew install nginx 68 | - 此处可以直接百度搜索 nginx 的下载安装过程 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 | 116 | 118 |
119 |
-------------------------------------------------------------------------------- /notelib/src/main/java/com/bacchus/notelib/cnblog/Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件(学习笔记).md: -------------------------------------------------------------------------------- 1 | #### 关于 AudioRecord 2 | Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。 3 | 4 | 如果想简单地做一个录音机,录制成音频文件,则推荐使用 MediaRecorder,而如果需要对音频做进一步的算法处理、或者采用第三方的编码库进行压缩、以及网络传输等应用,则建议使用 AudioRecord,其实 MediaRecorder 底层也是调用了 AudioRecord 与 Android Framework 层的 AudioFlinger 进行交互的。 5 | 6 | 音频的开发,更广泛地应用不仅仅局限于本地录音,因此,我们需要重点掌握如何利用更加底层的 AudioRecord API 来采集音频数据(注意,使用它采集到的音频数据是原始的PCM格式,想压缩为mp3,aac等格式的话,还需要专门调用编码器进行编码)。 7 | 8 | #### AudioRecord 的参数配置 9 | - **audioSource:音频采集的输入源** 10 | 11 | 可选的值以常量的形式定义在 MediaRecorder.AudioSource 类中,常用的值包括: 12 | - [x] 1. DEFAULT (默认) 13 | - [x] 2. VOICE_RECOGNITION (用于语音识别,等同于 DEFAULT ) 14 | - [x] 3. MIC (由手机麦克风输入) 15 | - [x] 4. VOICE_COMMUNICATION (用于 VoIP 应用)等等 16 | 17 | - **sampleRateInHz: 采样率** 18 | 19 | 目前 44100Hz 是唯一可以保证兼容所有 Android 手机的采样率。 20 | 21 | - **channelConfig: 通道数** 22 | 23 | 可选的值以常量的形式定义在 AudioFormat 类中,常用的是 24 | - [x] 1. CHANNEL_IN_MONO (单通道) 25 | - [x] 2. CHANNEL_IN_STEREO(双通道) 26 | 27 | - **audioFormat: 数据位宽** 28 | 29 | 可选的值以常量的形式定义在 AudioFormat 类中,常用的是( 1 是可以保证兼容所有Android手机的) 30 | - [x] 1. ENCODING_PCM_16BIT(16bit) 31 | - [x] 2. ENCODING_PCM_8BIT(8bit) 32 | 33 | - **bufferSizeInBytes:AudioRecord 内部的音频缓冲区的大小** 34 | 35 | 该缓冲区的值不能低于一帧“音频帧”(Frame)的大小: 36 | 37 | > int size = 采样率 x 位宽 x 采样时间 x 通道数 38 | 39 | 采样时间一般取 2.5ms~120ms 之间,由厂商或者具体的应用决定,我们其实可以推断,每一帧的采样时间取得越短,产生的延时就应该会越小,当然,碎片化的数据也就会越多。 40 | 41 | 在Android开发中,AudioRecord 类提供了一个帮助你确定这个 bufferSizeInBytes 的函数,原型如下: 42 | 43 | > int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat); 44 | 45 | 不同的厂商的底层实现是不一样的,但无外乎就是根据上面的计算公式得到一帧的大小,音频缓冲区的大小则必须是一帧大小的2~N倍。实际开发中,强烈建议由该函数计算出需要传入的 bufferSizeInBytes,而不是自己手动计算。 46 | 47 | #### 流程 48 | 49 | - **设置所有 AudioRecord 参数** 50 | 51 | ``` 52 | // 音频输入-麦克风 53 | private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC; 54 | // 采样频率 一般共分为 22.05KHz、44.1KHz、48KHz 三个等级 55 | // 44100 是目前的标准,但是某些设备仍然支持 22050,16000,11025 56 | private final static int AUDIO_SAMPLE_RATE = 44100; 57 | // 声道 单声道 58 | private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO; 59 | // 编码 60 | private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 61 | // 缓冲区字节大小 62 | private int bufferSizeInBytes = 0; 63 | // 录音对象 64 | private AudioRecord audioRecord; 65 | ``` 66 | - **创建 AudioRecord 并且获取缓冲区字节大小** 67 | 68 | ``` 69 | /** 70 | * 创建默认的录音对象 71 | * 72 | * @param fileName 文件名 73 | */ 74 | public void createDefaultAudio(String fileName) { 75 | // 获得缓冲区字节大小,AudioRecord 能接受的最小的 buffer 大小 76 | bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING); 77 | // 创建默认的录音对象并且设置一系列配置 78 | audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes); 79 | this.fileName = fileName; 80 | } 81 | ``` 82 | - **创建 buffer (用于保存新的声音数据),设置 buffer 大小(录制声音数据容量大小)** 83 | 84 | ``` 85 | // new 一个 byte 数组用来存一些字节数据,大小为缓冲区大小 86 | byte[] audiodata = new byte[bufferSizeInBytes]; 87 | ``` 88 | 89 | - **开始录音** 90 | 91 | ``` 92 | audioRecord.startRecording(); 93 | // 标志位, 用于控制停止数据流读写循环 94 | isRecording = true; 95 | ``` 96 | - **开始采集,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流** 97 | 98 | ``` 99 | // 开个子线程将录音的数据放入pcm文件 100 | new Thread(new Runnable() { 101 | @Override 102 | public void run() { 103 | writeDataTOFile(listener); 104 | } 105 | }).start(); 106 | 107 | // 如何将音频写入文件是重点,我写个伪代码,说明这个代码运行顺序 108 | // 首先创建 pcm 文件,得到他的 FileOutputStream,然后不断循环 AudioRecord 通过 read 将录音的数据放入字节数组里, 109 | // 当录音结束的时候要记得停止这个循环 110 | FileOutputStream fos = null; 111 | int readsize = 0; 112 | try { 113 | File file = new File(currentFileName); 114 | if (file.exists()) { 115 | file.delete(); 116 | } 117 | // 建立一个可存取字节的文件 118 | fos = new FileOutputStream(file); 119 | } catch (IllegalStateException e) { 120 | Log.e("AudioRecorder", e.getMessage()); 121 | throw new IllegalStateException(e.getMessage()); 122 | } catch (FileNotFoundException e) { 123 | Log.e("AudioRecorder", e.getMessage()); 124 | } 125 | while (isRecording) { 126 | readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes); 127 | if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) { 128 | try { 129 | fos.write(audiodata); 130 | } catch (IOException e) { 131 | Log.e("AudioRecorder", e.getMessage()); 132 | } 133 | } 134 | } 135 | ``` 136 | - **关闭数据流** 137 | 138 | 修改标志位:isRecording 为false,上面的while循环就自动停止了,数据流也就停止流动了,Stream也就被关闭了。 139 | ``` 140 | isRecording = false; 141 | ``` 142 | - **停止录音** 143 | 144 | 只要AudioRecord.stop就可以暂停录音了,然后当继续录音时在AudioRecord.start就好了,但是要另创建一个pcm记录,当录音结束时我们要将这些pcm一起转换为一个wav, 145 | 146 | 至于pcm转换为wav的代码是固定的 147 | 停止录音之后,注意要释放资源。 148 | 149 | ``` 150 | if (null != audioRecord) { 151 | audioRecord.stop(); 152 | audioRecord.release(); 153 | audioRecord = null; 154 | recordingThread = null; 155 | } 156 | ``` 157 | #### 流程走向 158 | - buffer 从 AudioRecord 中拉取声音数据 159 | 160 | ![拉取数据](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/cnblog_pull.png) 161 | 162 | - 创建一个数据流,开启一个线程,一边从 AudioRecord 中读取数据,一边将数据导入到数据流中 163 | 164 | ![导出数据](https://github.com/Bacchuc/AudioVideo/blob/master/notelib/src/main/java/com/bacchus/notelib/image/cnblog_push.png) 165 | 166 | #### 参考文献 167 | - [Android音频开发(2):如何采集一帧音频](https://blog.51cto.com/ticktick/1749719) 168 | - [Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件](https://www.cnblogs.com/renhui/p/7457321.html) 169 | - [Android 音视频深入](https://www.cnblogs.com/jianpanwuzhe/p/8403784.html) 170 | 171 | 172 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | --------------------------------------------------------------------------------