Skip to content

Commit 1e657f3

Browse files
Merge pull request #41 from SimformSolutionsPvtLtd/develop
Release v2.0.0
2 parents 62e8d0a + ffab2a3 commit 1e657f3

File tree

17 files changed

+188
-101
lines changed

17 files changed

+188
-101
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,15 @@ You can check out the full example at [Example](./example/src/App.tsx).
129129
| path\* | - ||| string | Used for `static` type. It is the resource path of an audio source file. |
130130
| candleSpace | 2 ||| number | Space between two candlesticks of waveform |
131131
| candleWidth | 5 ||| number | Width of single candlestick of waveform |
132+
| candleHeightScale | 3 ||| number | Scaling height of candlestick of waveform |
132133
| containerStyle | - ||| `StyleProp<ViewStyle>` | style of the container |
133134
| waveColor | #545454 ||| string | color of candlestick of waveform |
134135
| scrubColor | #7b7b7b ||| string | color of candlestick of waveform which has played |
135136
| onPlayerStateChange | - ||| ( playerState : PlayerState ) => void | callback function, which returns player state whenever player state changes. |
136137
| onPanStateChange | - ||| ( panMoving : boolean ) => void | callback function which returns boolean indicating whether audio seeking is active or not. |
137138
| onRecorderStateChange | - ||| ( recorderState : RecorderState ) => void | callback function which returns the recorder state whenever the recorder state changes. Check RecorderState for more details |
139+
| onCurrentProgressChange | - ||| ( currentProgress : number, songDuration: number ) => void | callback function, which returns current progress of audio and total song duration. |
140+
| onChangeWaveformLoadState | - ||| ( state : boolean ) => void | callback function which returns the loading state of waveform candlestick. |
138141
| onError | - ||| ( error : Error ) => void | callback function which returns the error for static audio waveform |
139142

140143
##### Know more about [ViewStyle](https://reactnative.dev/docs/view-style-props), [PlayerState](#playerstate), and [RecorderState](#recorderstate)
@@ -318,6 +321,8 @@ To use example app you need to first run below command
318321
cd example && npx react-native-asset
319322
```
320323

324+
> Note: If link-assets-manifest.json file already exists then make sure to delete that before running npx react-native-asset command.
325+
321326
This command will add our example audio sample files to the iOS bundle so that we can access them inside the iOS app.
322327

323328
```sh

android/src/main/java/com/audiowaveform/AudioPlayer.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,28 @@ class AudioPlayer(
4747
}
4848
}
4949
if (state == Player.STATE_ENDED) {
50-
val args: MutableMap<String, Any?> = HashMap()
50+
val args: WritableMap = Arguments.createMap()
5151
when (finishMode) {
5252
FinishMode.Loop -> {
5353
player.seekTo(0)
5454
player.play()
55-
args[Constants.finishType] = 0
55+
args.putInt(Constants.finishType, 0)
5656
}
5757
FinishMode.Pause -> {
5858
player.seekTo(0)
5959
player.playWhenReady = false
6060
stopListening()
61-
args[Constants.finishType] = 1
61+
args.putInt(Constants.finishType, 1)
6262
}
6363
else -> {
6464
player.stop()
6565
player.release()
6666
stopListening()
67-
args[Constants.finishType] = 2
67+
args.putInt(Constants.finishType, 2)
6868
}
6969
}
70-
args[Constants.playerKey] = key
70+
args.putString(Constants.playerKey, key)
71+
appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("onDidFinishPlayingAudio", args)
7172
}
7273
}
7374
}

android/src/main/java/com/audiowaveform/AudioRecorder.kt

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,34 @@ class AudioRecorder {
5454

5555
fun getDecibel(recorder: MediaRecorder?): Double? {
5656
if (useLegacyNormalization) {
57-
val db = 20 * log10((recorder?.maxAmplitude?.toDouble() ?: (0.0 / 32768.0)))
58-
if (db == Double.NEGATIVE_INFINITY) {
59-
Log.e(Constants.LOG_TAG, "Microphone might be turned off")
60-
} else {
61-
return db
57+
if (recorder != null) {
58+
try {
59+
val db = 20 * log10((recorder?.maxAmplitude?.toDouble() ?: (0.0 / 32768.0)))
60+
if (db == Double.NEGATIVE_INFINITY) {
61+
Log.e(Constants.LOG_TAG, "Microphone might be turned off")
62+
} else {
63+
return db
64+
}
65+
return db;
66+
} catch (e: IllegalStateException) {
67+
e.printStackTrace()
68+
return null
69+
}
70+
}
71+
else {
72+
return null
6273
}
63-
return db;
6474
} else {
65-
return recorder?.maxAmplitude?.toDouble() ?: 0.0
75+
if (recorder != null) {
76+
try {
77+
return recorder?.maxAmplitude?.toDouble() ?: 0.0
78+
} catch (e: IllegalStateException) {
79+
e.printStackTrace()
80+
return null
81+
}
82+
} else {
83+
return null
84+
}
6685
}
6786
}
6887

@@ -72,21 +91,29 @@ class AudioRecorder {
7291
encoder: Int,
7392
outputFormat: Int,
7493
sampleRate: Int,
75-
bitRate: Int?,
94+
bitRate: Int,
7695
promise: Promise
7796
) {
97+
if (recorder == null) {
98+
promise.reject("RECORDER_NULL", "MediaRecorder instance is null")
99+
return
100+
}
101+
78102
recorder?.apply {
79-
setAudioSource(MediaRecorder.AudioSource.MIC)
80-
setOutputFormat(getOutputFormat(outputFormat))
81-
setAudioEncoder(getEncoder(encoder))
82-
setAudioSamplingRate(sampleRate)
83-
if (bitRate != null) {
84-
setAudioEncodingBitRate(bitRate)
85-
}
86-
setOutputFile(path)
87103
try {
104+
setAudioSource(MediaRecorder.AudioSource.MIC)
105+
setOutputFormat(getOutputFormat(outputFormat))
106+
setAudioEncoder(getEncoder(encoder))
107+
setAudioSamplingRate(sampleRate)
108+
if (bitRate != null) {
109+
setAudioEncodingBitRate(bitRate)
110+
}
111+
setOutputFile(path)
88112
prepare()
89113
promise.resolve(true)
114+
} catch (e: IllegalArgumentException) {
115+
Log.e(Constants.LOG_TAG, "Invalid MediaRecorder configuration", e)
116+
promise.reject("CONFIGURATION_ERROR", "Invalid MediaRecorder configuration: ${e.message}")
90117
} catch (e: IOException) {
91118
Log.e(Constants.LOG_TAG, "Failed to stop initialize recorder")
92119
}
@@ -103,10 +130,10 @@ class AudioRecorder {
103130
val tempArrayForCommunication : MutableList<String> = mutableListOf()
104131
val duration = getDuration(path)
105132
tempArrayForCommunication.add(path)
106-
tempArrayForCommunication.add(duration)
133+
tempArrayForCommunication.add(duration.toString())
107134
promise.resolve(Arguments.fromList(tempArrayForCommunication))
108135
} catch (e: IllegalStateException) {
109-
Log.e(Constants.LOG_TAG, "Failed to stop recording")
136+
Log.e(Constants.LOG_TAG, "Failed to stop recording",e)
110137
}
111138
}
112139

@@ -127,7 +154,9 @@ class AudioRecorder {
127154
fun startRecorder(recorder: MediaRecorder?, useLegacy: Boolean, promise: Promise) {
128155
try {
129156
useLegacyNormalization = useLegacy
130-
recorder?.start()
157+
recorder?.apply {
158+
start()
159+
}
131160
promise.resolve(true)
132161
} catch (e: IllegalStateException) {
133162
Log.e(Constants.LOG_TAG, "Failed to start recording")

android/src/main/java/com/audiowaveform/AudioWaveformModule.kt

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
2929
private var path: String? = null
3030
private var outputFormat: Int = 0
3131
private var sampleRate: Int = 44100
32-
private var bitRate: Int? = null
32+
private var bitRate: Int = 128000
3333
private val handler = Handler(Looper.getMainLooper())
3434

3535
companion object {
@@ -51,8 +51,8 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
5151
}
5252

5353
@ReactMethod
54-
fun initRecorder(promise: Promise) {
55-
checkPathAndInitialiseRecorder(encoder, outputFormat, sampleRate, bitRate, promise)
54+
fun initRecorder(obj: ReadableMap?, promise: Promise) {
55+
checkPathAndInitialiseRecorder(encoder, outputFormat, sampleRate, bitRate, promise, obj)
5656
}
5757

5858
@ReactMethod
@@ -62,7 +62,7 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
6262

6363
@ReactMethod
6464
fun startRecording(obj: ReadableMap?, promise: Promise) {
65-
initRecorder(promise)
65+
initRecorder(obj, promise)
6666
val useLegacyNormalization = true
6767
audioRecorder.startRecorder(recorder, useLegacyNormalization, promise)
6868
startEmittingRecorderValue()
@@ -84,9 +84,15 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
8484

8585
@ReactMethod
8686
fun stopRecording(promise: Promise) {
87+
if (audioRecorder == null || recorder == null || path == null) {
88+
promise.reject("STOP_RECORDING_ERROR", "Recording resources not properly initialized")
89+
return
90+
}
91+
8792
audioRecorder.stopRecording(recorder, path!!, promise)
8893
stopEmittingRecorderValue()
8994
recorder = null
95+
path = null
9096
}
9197

9298
@ReactMethod
@@ -231,10 +237,12 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
231237
key = playerKey,
232238
extractorCallBack = object : ExtractorCallBack {
233239
override fun onProgress(value: Float) {
234-
if (value == 1.0F) {
235-
val tempArrayForCommunication : MutableList<MutableList<Float>> = mutableListOf()
236-
extractors[playerKey]?.sampleData?.let { tempArrayForCommunication.add(it) }
237-
promise.resolve(Arguments.fromList(tempArrayForCommunication))
240+
if (value == 1.0F) {
241+
extractors[playerKey]?.sampleData?.let { data ->
242+
val normalizedData = normalizeWaveformData(data, 0.12f)
243+
val tempArrayForCommunication: MutableList<MutableList<Float>> = mutableListOf(normalizedData)
244+
promise.resolve(Arguments.fromList(tempArrayForCommunication))
245+
}
238246
}
239247
}
240248
},
@@ -244,6 +252,16 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
244252
extractors[playerKey]?.stop()
245253
}
246254

255+
private fun normalizeWaveformData(data: MutableList<Float>, scale: Float = 0.25f, threshold: Float = 0.01f): MutableList<Float> {
256+
val filteredData = data.filter { kotlin.math.abs(it) >= threshold }
257+
val maxAmplitude = filteredData.maxOrNull() ?: 1.0f
258+
return if (maxAmplitude > 0) {
259+
data.map { if (kotlin.math.abs(it) < threshold) 0.0f else (it / maxAmplitude) * scale }.toMutableList()
260+
} else {
261+
data
262+
}
263+
}
264+
247265
private fun getUpdateFrequency(frequency: Int?): UpdateFrequency {
248266
if (frequency == 2) {
249267
return UpdateFrequency.High
@@ -257,9 +275,24 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
257275
encoder: Int,
258276
outputFormat: Int,
259277
sampleRate: Int,
260-
bitRate: Int?,
261-
promise: Promise
278+
bitRate: Int,
279+
promise: Promise,
280+
obj: ReadableMap?
262281
) {
282+
283+
var sampleRateVal = sampleRate.toInt();
284+
var bitRateVal = bitRate.toInt();
285+
286+
if(obj != null) {
287+
if(obj.hasKey(Constants.bitRate)){
288+
bitRateVal = obj.getInt(Constants.bitRate);
289+
}
290+
291+
if(obj.hasKey(Constants.sampleRate)){
292+
sampleRateVal = obj.getInt(Constants.sampleRate);
293+
}
294+
}
295+
263296
try {
264297
recorder = MediaRecorder()
265298
} catch (e: Exception) {
@@ -278,8 +311,8 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
278311
recorder,
279312
encoder,
280313
outputFormat,
281-
sampleRate,
282-
bitRate,
314+
sampleRateVal,
315+
bitRateVal,
283316
promise,
284317
)
285318
} catch (e: IOException) {
@@ -291,8 +324,8 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav
291324
recorder,
292325
encoder,
293326
outputFormat,
294-
sampleRate,
295-
bitRate,
327+
sampleRateVal,
328+
bitRateVal,
296329
promise,
297330
)
298331
}

android/src/main/java/com/audiowaveform/Utils.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ object Constants {
4141
const val waveformData = "waveformData"
4242
const val updateFrequency = "updateFrequency"
4343
const val currentDecibel = "currentDecibel"
44+
const val bitRate = "bitRate"
45+
const val sampleRate = "sampleRate"
4446
}
4547

4648
enum class FinishMode(val value:Int) {

example/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ DerivedData
2121
*.ipa
2222
*.xcuserstate
2323
ios/.xcode.env.local
24+
ios/link-assets-manifest.json
2425

2526
# Android/IntelliJ
2627
#
@@ -31,6 +32,7 @@ local.properties
3132
*.iml
3233
*.hprof
3334
.cxx/
35+
android/link-assets-manifest.json
3436

3537
# node.js
3638
#

example/android/link-assets-manifest.json

Lines changed: 0 additions & 25 deletions
This file was deleted.

example/ios/link-assets-manifest.json

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)