Skip to content

Commit

Permalink
Add a recorder (#1)
Browse files Browse the repository at this point in the history
* ♻️ Refactor MainActivity to implement new feature

* ⚗️ Experiment with AudioRecord

* 🚧 Try to make MediaRecorder work

Something is wrong "mediarecorder went away with unhandled events"

* ⚗️ Experiment with MediaRecorder

* ⚗️ Provide a working example with SurfaceView

* ⚗️ Introduce a partly-working view

* ✨ Support recording a sound

* 🍻 Add audio effects. Fix chronometer position

* ⚡ Replace Chronometer by a CountDownTimer

* 🐛 Fix the line not going to the end of the view

* ♻️ Clean up code

* ⚡ Clean up files not saved

* 🐛 Fix destroyrecorder crashing the app

* 📱 Use correctly compound view

Need to update the PlayerView

* ⚡ Replace constraintlayout by merge tag

* 💄 Make minor graphical adjustments

* ✨ Add a possibility of metadata retriever

Usually this would be a viewModel

* 💄 Add control buttons

* 🐛 Fix the sound not being recorded properly

* 🐛 Fix multiple recording and play causing crash

* 🎨 Place player inside recordercontroller

* 💄 Support feedback when playing

* 🐛 Fix crash when destroyPlayer

* 🐛 Fix validate button not being properly used

* 🐛 Fix FileNotFoundException onStopRecording

* 🐛 Fix UI thread blocked when converting to wav

* 🐛 Fix recording line not going to the end

* 💄 Prevent activity from turning screen off

* 🔥 Delete files when not used

* 📝 Update README.md for 1.2.0
  • Loading branch information
Haransis authored Aug 14, 2020
1 parent 2705b14 commit 2675dc4
Show file tree
Hide file tree
Showing 29 changed files with 1,269 additions and 113 deletions.
51 changes: 42 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
</p>

# WaveFormPlayer
An Android library to create visualization of a given sound. This is a very basic implementation, use at your own risk.
An Android library to create visualization of a given sound. It also supports a wav recorder with sound wave visualization. This is a very basic implementation, use at your own risk.

Note that the library does not build the amplitudes by itself.
Note that the library does not build the amplitudes by itself when reading from a file.

## Installation
### Gradle
Expand All @@ -23,7 +23,7 @@ Add it in your root build.gradle at the end of repositories:
2. Add the dependency in Gradle
```
dependencies {
implementation 'com.github.Haransis:WaveFormPlayer:1.1.2'
implementation 'com.github.Haransis:WaveFormPlayer:1.2.0'
}
```

Expand All @@ -42,22 +42,22 @@ Add it in your root build.gradle at the end of repositories:
<dependency>
<groupId>com.github.Haransis</groupId>
<artifactId>WaveFormPlayer</artifactId>
<version>1.1.2</version>
<version>1.2.0</version>
</dependency>
```

## Usage
### Player
1. Add the player view in your layout :
```
<fr.haran.soundwave.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="200dp"
app:fontName="ibm_plex_mono_regular.ttf"
app:title="Music Title"
app:waveDb="true"
app:mainColor="@android:color/black"
app:secondaryColor="@android:color/darker_gray"/>
app:secondaryColor="@android:color/darker_gray"
... />
```

2. In your Activity, create a custom controller implementing the PlayerController interface or use the default one :
Expand Down Expand Up @@ -90,9 +90,42 @@ try {
```
view.addAudioUrl(url,amplitudes)
```
Note : you need to add the Internet permission in your manifest
The amplitudes are an array of double inside [-1.0; 1.0].

For a more detailed use case application, please have a look at the example app in the repository.
4. Do not forget to destroy the player inside the OnDestroy method of your Activity.
```
playerController.destroyPlayer()
```

### Recorder
The recorder ressembles to the player a lot but here is a quick tutorial.
1. Add the recorder view in your layout
```
<fr.haran.soundwave.ui.RecPlayerView
android:id="@+id/rec_player_view"
app:rec_color="@android:color/black"
app:rec_playedColor="@android:color/darker_gray"
... />
```

2. In your Activity, create a custom controller implementing the RecorderController interface or use the default one (You need to reference the view, and the path where the files will be created) :
```
recorderController = DefaultRecorderController(findViewById(R.id.rec_player_view), applicationContext.externalCacheDir?.absolutePath?)
```

3. Set a listener and prepare the controller
```
recorderController.setRecorderListener(validate = {...})
recorderController.prepareRecorder()
```
Using the default listener is perfectly fine but you should at least use the validate function to use the files.

4. Do not forget to destroy the recorder inside the OnDestroy method of your Activity.
```
recorderController.destroyPlayer()
```
For a more detailed implementation, please have a look at the example app in the repository.

## Building the amplitudes
As mentionned before, for now this library does not implement the calculation of the amplitudes so you should implement this by yourself. For my use case, a static array was enough.
Moreover the formats the recorder and the player uses are different. This will be solved soon.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
10 changes: 8 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.haran.example">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="fr.haran.example.MainActivity">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RecActivity" />
<activity android:name=".PlayActivity" />
</application>

</manifest>
42 changes: 9 additions & 33 deletions app/src/main/java/fr/haran/example/MainActivity.kt

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions app/src/main/java/fr/haran/example/PlayActivity.kt

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions app/src/main/java/fr/haran/example/RecActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package fr.haran.example

import android.os.Bundle
import android.view.KeyEvent
import android.view.WindowManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import fr.haran.soundwave.controller.DefaultRecorderController
import java.io.File

private const val TAG = "RecActivity"
class RecActivity : AppCompatActivity(), DefaultRecorderController.InformationRetriever {

private var recorderController: DefaultRecorderController? = null
private lateinit var soundPath: String
private lateinit var soundAmplitudes: List<Int>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_rec)
recorderController = applicationContext.externalCacheDir?.absolutePath?.let { it ->
DefaultRecorderController(findViewById(R.id.rec_player_view),
it, this
)
}
recorderController?.setRecorderListener(
start = { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) },
complete = { window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)},
validate = {
Toast.makeText(
this@RecActivity,
"Sound Recorded !",
Toast.LENGTH_SHORT
).show()}
)
recorderController?.prepareRecorder()
}

override fun onDestroy() {
super.onDestroy()
recorderController?.let {
val wavFile = File(it.wavPath)
if (wavFile.exists())
wavFile.canonicalFile.delete()
it.destroyController()
recorderController = null
}
}

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish()
}
return super.onKeyDown(keyCode, event)
}

override fun setPath(path: String) {
soundPath = path
}

override fun setAmplitudes(amplitudes: List<Int>) {
soundAmplitudes = amplitudes
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_mic.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_stop.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M6,6h12v12H6z"/>
</vector>
36 changes: 15 additions & 21 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,26 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
tools:context=".MainActivity">

<TextView
android:id="@+id/hello_world"
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/play"
android:text="@string/playerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amplitude simple"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.233" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/rec"/>

<fr.haran.soundwave.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="200dp"
app:title="Music Title"
app:waveDb="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/hello_world"
app:mainColor="@android:color/black"
app:secondaryColor="@android:color/darker_gray"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/rec"
android:text="@string/recordingview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/play"
app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
35 changes: 35 additions & 0 deletions app/src/main/res/layout/activity_play.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
tools:context=".PlayActivity">

<TextView
android:id="@+id/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PlayerView"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.233" />

<fr.haran.soundwave.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="200dp"
app:title="Music Title"
app:waveDb="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/hello_world"
app:mainColor="@android:color/black"
app:secondaryColor="@android:color/darker_gray"/>

</androidx.constraintlayout.widget.ConstraintLayout>
21 changes: 21 additions & 0 deletions app/src/main/res/layout/activity_rec.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RecActivity">

<fr.haran.soundwave.ui.RecPlayerView
android:id="@+id/rec_player_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:rec_color="@android:color/black"
app:rec_playedColor="@android:color/darker_gray"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Example</string>
<string name="playerview">PlayerView</string>
<string name="recordingview">RecordingView</string>
</resources>
4 changes: 2 additions & 2 deletions soundwave/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
defaultConfig {
minSdkVersion 25
targetSdkVersion 29
versionCode 5
versionName "1.1.2"
versionCode 6
versionName "1.2.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Loading

0 comments on commit 2675dc4

Please sign in to comment.