GithubHelp home page GithubHelp logo

tchvu3 / capacitor-voice-recorder Goto Github PK

View Code? Open in Web Editor NEW
67.0 4.0 54.0 4.93 MB

Capacitor plugin for voice recording

Ruby 2.21% Java 34.42% Swift 22.54% Objective-C 2.81% JavaScript 7.86% TypeScript 26.72% HTML 3.43%

capacitor-voice-recorder's Introduction

Capacitor Voice Recorder

tchvu3/capacitor-voice-recorder

Capacitor plugin for simple voice recording (For Capacitor 5)


Maintainers

Maintainer GitHub
Avihu Harush tchvu3

Installation

npm install --save capacitor-voice-recorder
npx cap sync

ios note

Make sure to include the NSMicrophoneUsageDescription key, and a corresponding purpose string in your app's Info.plist

Configuration

No configuration required for this plugin.

Supported methods

Name Android iOS Web
canDeviceVoiceRecord
requestAudioRecordingPermission
hasAudioRecordingPermission
startRecording
stopRecording
pauseRecording
resumeRecording
getCurrentStatus

Explanation

  • canDeviceVoiceRecord - on mobile this function will always return a promise that resolves to { value: true }, while in a browser it will be resolved to { value: true } / { value: false } based on the browser's ability to record. note that this method does not take into account the permission status, only if the browser itself is capable of recording at all.

  • requestAudioRecordingPermission - if the permission has already been provided then the promise will resolve with { value: true }, otherwise the promise will resolve to { value: true } / { value: false } based on the answer of the user to the request.

  • hasAudioRecordingPermission - will resolve to { value: true } / { value: false } based on the status of the permission. please note that the web implementation of this plugin uses the Permissions API under the hood which is not widespread as of now. as a result, if the status of the permission cannot be checked the promise will reject with COULD_NOT_QUERY_PERMISSION_STATUS. in that case you have no choice but to use the requestAudioRecordingPermission function straight away or startRecording and capture any exception that is thrown.

  • startRecording - if the app lacks the required permission then the promise will reject with the message MISSING_PERMISSION. if the current device cannot voice record at all (for example, due to old browser) then the promise will reject with DEVICE_CANNOT_VOICE_RECORD. if there's a recording already running then the promise will reject with ALREADY_RECORDING, and if other apps are using the microphone then the promise will reject with MICROPHONE_BEING_USED. in a case of unknown error the promise will reject with FAILED_TO_RECORD.

  • stopRecording - will stop the recording that has been previously started. if the function startRecording has not been called beforehand the promise will reject with RECORDING_HAS_NOT_STARTED. if the recording has been stopped immediately after it has been started the promise will reject with EMPTY_RECORDING. in a case of unknown error the promise will reject with FAILED_TO_FETCH_RECORDING. in case of success, you will get the recording in base-64, the duration of the recording in milliseconds, and the mime type.

  • pauseRecording - will pause an ongoing recording. note that if the recording has not started yet the promise will reject with RECORDING_HAS_NOT_STARTED. in case of success the promise will resolve to { value: true } if the pause was successful or { value: false } if the recording is already paused. note that on certain mobile os versions this function is not supported. in these cases the function will reject with NOT_SUPPORTED_OS_VERSION and your only viable options is to stop the recording instead.

  • resumeRecording - will resume a paused recording. note that if the recording has not started yet the promise will reject with RECORDING_HAS_NOT_STARTED. in case of success the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running. note that on certain mobile os versions this function is not supported. in these cases the function will reject with NOT_SUPPORTED_OS_VERSION and your only viable options is to stop the recording instead

  • getCurrentStatus - will let you know the current status of the current recording (if there is any at all). will resolve with one of the following values: { status: "NONE" } if the plugin is idle and waiting to start a new recording. { status: "RECORDING" } if the plugin is in the middle of recording and { status: "PAUSED" } if the recording is paused right now.

Usage


// only 'VoiceRecorder' is mandatory, the rest is for typing
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder';

// will print true / false based on the ability of the current device (or web browser) to record audio
VoiceRecorder.canDeviceVoiceRecord().then((result: GenericResponse) => console.log(result.value))

/**
* will prompt the user to give the required permission, after that
* the function will print true / false based on the user response
*/
VoiceRecorder.requestAudioRecordingPermission().then((result: GenericResponse) => console.log(result.value))

/**
* will print true / false based on the status of the recording permission.
* the promise will reject with "COULD_NOT_QUERY_PERMISSION_STATUS"
* if the current device cannot query the current status of the recording permission
*/
VoiceRecorder.hasAudioRecordingPermission.then((result: GenericResponse) => console.log(result.value))

/**
* In case of success the promise will resolve to { value: true }
* in case of an error the promise will reject with one of the following messages:
* "MISSING_PERMISSION", "ALREADY_RECORDING", "MICROPHONE_BEING_USED", "DEVICE_CANNOT_VOICE_RECORD", or "FAILED_TO_RECORD"
*/
VoiceRecorder.startRecording()
.then((result: GenericResponse) => console.log(result.value))
.catch(error => console.log(error))

/**
* In case of success the promise will resolve to:
* {"value": { recordDataBase64: string, msDuration: number, mimeType: string }},
* the file will be in one of several possible formats (more on that later).
* in case of an error the promise will reject with one of the following messages:
* "RECORDING_HAS_NOT_STARTED" or "FAILED_TO_FETCH_RECORDING"
*/
VoiceRecorder.stopRecording()
.then((result: RecordingData) => console.log(result.value))
.catch(error => console.log(error))

/**
* will pause an ongoing recording. note that if the recording has not started yet the promise
* will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the pause
* was successful or `{ value: false }` if the recording is already paused.
* if the current mobile os does not support this method the promise will reject with `NOT_SUPPORTED_OS_VERSION`
*/
VoiceRecorder.pauseRecording()
.then((result: GenericResponse) => console.log(result.value))
.catch(error => console.log(error))

/**
* will resume a paused recording. note that if the recording has not started yet the promise
* will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the resume
* was successful or `{ value: false }` if the recording is already running.
* if the current mobile os does not support this method the promise will reject with `NOT_SUPPORTED_OS_VERSION`
*/
VoiceRecorder.resumeRecording()
.then((result: GenericResponse) => console.log(result.value))
.catch(error => console.log(error))

/**
* Will return the current status of the plugin.
* in this example one of these possible values will be printed: "NONE" / "RECORDING" / "PAUSED"
*/
VoiceRecorder.getCurrentStatus()
.then((result: CurrentRecordingStatus) => console.log(result.status))
.catch(error => console.log(error))

Format and Mime type

The plugin will return the recording in one of several possible formats. the format is dependent on the os / web browser that the user uses. on android and ios the mime type will be audio/aac, while on chrome and firefox it will be audio/webm;codecs=opus and on safari it will be audio/mp4. note that these 3 browsers has been tested on. the plugin should still work on other browsers, as there is a list of mime types that the plugin checks against the user's browser.

Note that this fact might cause unexpected behavior in case you'll try to play recordings between several devices or browsers - as they not all support the same set of audio formats. it is recommended to convert the recordings to a format that all your target devices supports. as this plugin focuses on the recording aspect, it does not provide any conversion between formats.

Playback

To play the recorded file you can use plain javascript:

const base64Sound = '...' // from plugin
const mimeType = '...'  // from plugin
const audioRef = new Audio(`data:${mimeType};base64,${base64Sound}`)
audioRef.oncanplaythrough = () => audioRef.play()
audioRef.load()

Donation

If you enjoy my work and find it useful, feel free to invite me to a cup of coffee :)

"Buy Me A Coffee"

capacitor-voice-recorder's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

capacitor-voice-recorder's Issues

Feature request: live steam/mic input while recording

I'm using wavesurfer.js to render the final recording. Could there be a way of providing a live stream, or mic volume to the app from this plugin? Has anyone done anything similar? So that the user can see that they are being recorded.

Thanks
Nic

problems with different mimetypes in different browsers (v4)

Hey,

i have a problem with the different encoding of the files. As far as i can tell the mime-types are different for each scenario: audio/webm (recorded with chrome, also different depending on which browser you record with.) for browser and audio/aac (with different sigantures) from android or apple devices.

We record the voice messages in different devices or different browsers and need to support as many platforms as possible, we upload them via aws s3 as the pure base64 string, then download them as these strings, create an instance of an Audio HTML object and play them. (current state)

The problem is that these files have for example different durations then the real duration or do not get played at all (only in safari an issue). The encoding of voice messages recorded by the android devices seem to work in every browser (we tested) for some reason.

Do you know of a way to record the voice messages with an encoding in e.g. Chrome that will work in Safari? Is there a way to convert this encoding, or record with a more broad possibilty to choose the encoding we want?

IOS error on build

Thank you for the plugin. It's a great help.
It works for Android great however at build time I receive the following error message in XCode. I tried what I could but I have no idea where the error comes from. Latest XCode. Anything known?

`Ld /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/CapacitorVoiceRecorder/CapacitorVoiceRecorder.framework/CapacitorVoiceRecorder normal (in target 'CapacitorVoiceRecorder' from project 'Pods')
cd /Users/peterszeman/Documents/chattutor/ios/App/Pods
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -target arm64-apple-ios12.0 -dynamiclib -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk -L/Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/CapacitorVoiceRecorder -F/Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/CapacitorVoiceRecorder -F/Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/Capacitor -F/Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/CapacitorCordova -filelist /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Intermediates.noindex/Pods.build/Release-iphoneos/CapacitorVoiceRecorder.build/Objects-normal/arm64/CapacitorVoiceRecorder.LinkFileList -install_name @rpath/CapacitorVoiceRecorder.framework/CapacitorVoiceRecorder -Xlinker -rpath -Xlinker /usr/lib/swift -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -rpath -Xlinker @loader_path/Frameworks -dead_strip -Xlinker -object_path_lto -Xlinker /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Intermediates.noindex/Pods.build/Release-iphoneos/CapacitorVoiceRecorder.build/Objects-normal/arm64/CapacitorVoiceRecorder_lto.o -fembed-bitcode-marker -fobjc-arc -fobjc-link-runtime -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -L/usr/lib/swift -Xlinker -add_ast_path -Xlinker /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Intermediates.noindex/Pods.build/Release-iphoneos/CapacitorVoiceRecorder.build/Objects-normal/arm64/CapacitorVoiceRecorder.swiftmodule -framework Capacitor -framework Foundation -compatibility_version 1 -current_version 1 -Xlinker -dependency_info -Xlinker /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Intermediates.noindex/Pods.build/Release-iphoneos/CapacitorVoiceRecorder.build/Objects-normal/arm64/CapacitorVoiceRecorder_dependency_info.dat -o /Users/peterszeman/Library/Developer/Xcode/DerivedData/App-beknefqbwxnrjvgtzlguknerdxdw/Build/Products/Release-iphoneos/CapacitorVoiceRecorder/CapacitorVoiceRecorder.framework/CapacitorVoiceRecorder

Undefined symbols for architecture arm64:
"OBJC_CLASS$_VoiceRecorderPlugin", referenced from:
_OBJC$CATEGORY_VoiceRecorderPlugin$_CAPPluginCategory in VoiceRecorder-325378aea09476210eff33d5adb008ecf712f34df2b415ce0d8240f209ac93ab.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
asdf
`

Android canDeviceVoiceRecord gives false negative

Checking out the Android version canDeviceVoiceRecord function it gives false while I can still record.
Looking at the code itself, I don't fully understand how it works.
As I can see it tries to create a MediaRecorder and then tries to start and stop it. However if I run this before asking for permission it will always fail.
So basically I don't see the purpose of the function, and in its current position (before asking for permission) it took me a lot of time to realize that I should simply not use this for right now.

Sending via http post request

Hi @tchvu3 ,
Please how can I go about sending the data returned on recording stop as a file via HTTP post request?
I have tried various ways to convert the base64 data into a blob file that can be sent as form data but I think I'm missing something because every day I've tried has either led to an error or a corrupted file that cannot be played.

I would appreciate it if you can give me a summary of how you intended for the data to be transferred via HTTP requests.
Thank you in advance.

How to prompt user for permission in order to avoid `MISSING_PERMISSION` error?

Hey there, great lib, thanks for making it.

How do I prompt the user for permission to record in order to avoid the MISSING_PERMISSION error? Do I have to manually bring up that prompt by calling a method on the VoiceRecording object? Or should Android/iOS just do it automatically once it detects permissions in the AndroidManifest.xml/Info.plist?

If the case is the latter, then what permissions do we need to add in order to voice record?

Unexpectedly found nil crash

I've found this plugin fatally crashes when my phone is in an audio call and I try to record an audio. I've got everything wrapped in try catch blocks and it still fatally crashes the app.

Xcode throws an error here, something about trying to implicitly unwrap nil

    public func stopRecording() {
        do {
            audioRecorder.stop()
            try recordingSession.setActive(false)
            try recordingSession.setCategory(originalRecordingSessionCategory)
            originalRecordingSessionCategory = nil
            audioRecorder = nil
            recordingSession = nil
            status = CurrentRecordingStatus.NONE
        } catch {}
    }
    ```

Support Capacitor v4

Support Capacitor v4

import VoiceRecorder from Plugins, we have this deprecation:

@deprecated
Provided for backwards compatibility for Capacitor v2 plugins. Capacitor v3 plugins should import the plugin directly. This "Plugins" export is deprecated in v3, and will be removed in v4.

import VoiceRecorder from capacitor-voice-recorder, we have this error on native devices:
Error: VoiceRecorder does not have web implementation

How I can get audioCotext raw data ?

How I can get audioCotext raw data While recording ?
I need to get audio Contex raw data for using in javascripts method like
createAnalyser
and
frequencyBinCount

Chould you help me to modify the plugin for adding the method for rethrn raws audio contex to my ja file and in js listen to that ?

i wamt to create visualyser from microphone audio but I wont to use webAPI navigator for audio recorder … I think I should modify this plugin because everything of this plugin works true just need method for return audio contex as real-time
Thank you

Audio is not recorded if device is locked on Android 14

How to reproduce

  1. Start recording
  2. Lock screen (in my case Pixel 7 with Android 14)
  3. Unlock screen

After these actions recorded data has empty samples during screen is locked

How to solve

Based on sources which I read

Useful sources

So as I understand new api requires Foreground Service with persistent notification. It would be nice if this library supports background recording on new android

Feature Request: Provide option to not save recording to file

Hello!
We're doing a POC that has a special restriction on recorded audio: the audio cannot be saved to the device. Wanted to see how hard it would be to add an option for returning the raw recording data as base64 or a data URL instead of saving it to the device temp directory. Here's how the official Capacitor camera plugin handles this for reference. Thanks!

https://github.com/ionic-team/capacitor-plugins/blob/main/camera/ios/Plugin/CameraPlugin.swift#L263

Error Message - 'VoiceRecorder has no web implementation'

I have followed the exact steps as provided in angular ionic project. However, this line ->
VoiceRecorder.canDeviceVoiceRecord().then((result: GenericResponse) => console.log(result.value));

is throwing exception -
"core.js:4197 ERROR Error: Uncaught (in promise): VoiceRecorder does not have web implementation."

In the documentation, it says that canDeviceVoiceRecord() will never throw an exception. Could you please confirm whether this plugin does not support web implementation or am I doing something wrong?

Thank you so much to look into this.

API enhencement request

This plugin is what I was searching for some time in order to replace the old deprecated cordova plugin media, but some functions are missing for a complete API:

  • Functions for pausing and resuming recording, it seems possible since the the MediaRecorder class support pause and resume methods.
  • Add parameters to VoiceRecorder.stopRecording to change the return value format. In my case I will record for dozens of minutes and the base64 encoded string will be really too large, it would be nice to have a parameter to specify that we want to move the temporary file to another location instead of returning its content. For example VoiceRecorder.stopRecording() could take an options object like
interface StopOptions { 
    move?: string;
}

In this case if move is != null, the plugin move the file to the given location (this could be an URI returned by Filesystem.getUri) and return this location instead of returning the file content as encoded string.

If you don't have enough time to do it, I will try to implement this (only for Android because I have no idea how to do this on iOS).

Unable to build on Capacitor 4

I have an ionic 6 project using Capacitor 4. When compiling the project, I get the error seen below:

C:\project\MainActivity.java:10: error: package com.tchvu3.capvoicerecorder does not exist
import com.tchvu3.capvoicerecorder.VoiceRecorder;

My package.json has this:

"capacitor-voice-recorder": "^4.0.1",

This was working when I was using an older version of Capacitor. I tried uninstalling and re-installing, but get the same error. Any help or suggestions would be much appreciated.

Capacitor 5 + Gradle 8 support

Seems like changes in the AndroidManifest.xml and build.gradle are required to make this plugin work on Capacitor 5 for Android with Gradle 8.

The package name needs to be removed from the manifest file and added to the build.gradle file as namespace field.

On iOS the plugin does not work well with other audio playback plugins

Upon recording start, the plugin sets the AVAudioSession category to record only which prevents the app from playing sounds with other audio plugins (e.g. native-audio).
My solution was to set the category to playAndRecord but I assume the proper solution is to save the current category and restore it after recording stops.

plugin not work in Web

Error: MISSING_PERMISSION
at missingPermissionError (predefined-web-responses.js?600f:3:1)
at VoiceRecorderImpl.startRecording (VoiceRecorderImpl.js?7f8d:31:1)

"ERR_REQUEST_RANGE_NOT_SATISFIABLE" error on stopRecording()

Hi,

I'm trying to record some audio in a web app and sometimes when I call stopRecording the error below appears.

image

I made sure that the recorder was running before pausing by checking if getCurrentStatus() returns 'RECORDING' or 'PAUSED'. This behaviour seems random because it is sometimes working and sometimes not. The error also doesn't get caught by any of the error listeners for the provided functions.

My Setup:
Chrome 107.0.5304.107 (Official build) (64-Bit)
"@capacitor/core": "^4.5.0"
"capacitor-voice-recorder": "^4.0.0"

Thanks for the help in advance
Philip

Limitations of the plugin

Hi there,

I'm looking to use this plugin in my next project and I wondered if there were any limitations in regards to the maximum duration of the audio file?

As I understand it from a previous question regarding file management the library creates a temp file which is then base64 encoded and then deleted. The base64 string is bubbled back up as a string to the web application and at this point is there a theoretical maximum size on this?

My use case is long audio recordings (1hr let's say) and I'm wondering if I'll face any issues.

Thanks in advance.

Question : How to truncate the b64 audio ?

Hello @tchvu3,

We are looking for truncate the generated audio. 🚀

  1. Truncate the B64String directly
const start = result.value.recordDataBase64.slice(
  0,
  delaiSequenceDebut
);
const end = result.value.recordDataBase64.slice(
  -delaiSequenceFin
);

const audioRef = new Audio(
  `data:${result.value.mimeType};base64,${deb+fin}`
);
audioRef.oncanplaythrough = () => audioRef.play();
audioRef.load();

--> We hear nothing

  1. Truncate an ArrayBuffer
// decoding
const binaryAudio = window.atob(result.value.recordDataBase64);
const lengthBinary = binaryAudio.length;
// To bytes
const byte = new Uint8Array(lengthBinary);
for (let i = 0; i < lengthBinary; i++) {
  byte[i] = binaryAudio.charCodeAt(i);
}
let arrayBuffer = byte.buffer;

// get the first 40%
const delaiSequenceDebut = arrayBuffer.byteLength * 0.4;
let arrayBufferStart = arrayBuffer.slice(
  0,
  delaiSequenceDebut
);

// get the last 45%
const delaiSequenceFin = arrayBuffer.byteLength * 0.45;
let arrayBufferEnd = arrayBuffer.slice(
  -delaiSequenceFin
);

// Concat the 2 previous arrays
const byteStart = new Uint8Array(arrayBufferStart);
const byteStop = new Uint8Array(arrayBufferEnd);
const tmp = new Uint8Array([...byteStart, ...byteStop]);

// convert to b64 
let newRecordDataBase64 = '';
var len = tmp.byteLength;
for (var i = 0; i < len; i++) {
  newRecordDataBase64 += String.fromCharCode(tmp[i]);
}
newRecordDataBase64 = window.btoa(newRecordDataBase64);

// playing the b64
const audioRef = new Audio(
  `data:${result.value.mimeType};base64,${newRecordDataBase64}`
);
audioRef.oncanplaythrough = () => audioRef.play();
audioRef.load();

--> We can hear the start but the ending part is corrupted

Do you kown how can we process this media ?

Thanks a lot,
Eddy & Julien

Poke @e-allais

Add LICENSE.

Hey I was thinking why the capacitor-community doesn't have a plugin for audio recording.
capacitor-community/media#12
I will create a PR in media plugin to add the same functionality.
For which I might take some help for here. But before I do that,
I'm guessing this is open-source. Can you add the LICENSE?

A quick suggestion,
I'm afraid of using this plugin cause of maintainability.
Maybe you can also ask capacitor community to add this repo officially in order to receive more contributors.

Cant get it to work on iOS

Hey there,

I try to implement a chat where the user can record voice messages.
I added your plugin via yarn add capacitor-voice-recorder and I have the following lines in my Info.plist:

<key>NSMicrophoneUsageDescription</key>
<string>To Record Audio Messages in Chat</string>

In my Vue file I have:

import { Plugins } from '@capacitor/core'

const { VoiceRecorder } = Plugins

VoiceRecorder.canDeviceVoiceRecord().then((result) => { alert('works') })

But VoiceRecorder is undefined on an iOS Emulator. There are other Plugins available, but not this one.
I guess there is something missing in the README. Because for Android you added the Plugin Class in the Android code.

UPDATE:
I'm using Capacitor 2.4.2 and it's also not working under Android. On Android's MainActivity.java it says:

Cannot resolve symbol "tchvu3"

On this line import com.tchvu3.capvoicerecorder.VoiceRecorder;

Can you please help me?

Thanks in advance
Nick

How to pause recording?

I'm developing an app in which user can pause recording voice and than resume the recording from same point where it was paused. Is it possible to achieve this functionality with this plugin?

[Help] Data stored in audio file doesn't playback

I am trying to utilize this library to:

  1. Record audio
  2. Play the audio locally before sending data to the server
  3. Send audio file to the server

Here's the current code:

In service

startRecording = ()=> {
        try {
            return VoiceRecorder.requestAudioRecordingPermission().then((result: GenericResponse) => {
                if (result.value) {
                    return VoiceRecorder.startRecording()
                } else {
                    return Promise.reject('Permission denied');
                }
            })
          } catch (error) {
            console.log(error);
            return Promise.reject(error);
        }
    }

stopRecording = async () => {
        console.log("stopRecording");
        let result = await VoiceRecorder.stopRecording();

        let blob = base64StringToBlob(result.value.recordDataBase64, result.value.mimeType);
        let data_text = await blob.text();
        let extension = mime.getExtension(result.value.mimeType);
        let write_result = await Filesystem.writeFile({
            path: `temp.${extension}`,
            data: data_text,
            directory: Directory.External,
            encoding: Encoding.UTF8,
        });
        let uri = write_result.uri;

        return uri;
    }

At this point I tested the locally stored file temp.aac. Sent the file to PC and tried to playback the audio but audio player gives corrupted or codec error (note tried multiple audio players). While .aac file downloaded from the internet works absolutely fine.

What have I tried till now:

  1. Tried storing data as shown above
  2. Tried forcing the extension to be .aac instead of .adts that mime.getExtension suggests
  3. Tried storing base64 data directly
  4. Tried storing data:${result.value.mimeType};base64,${result.value.recordDataBase64}

None of these have worked. Any suggestions would be appreciated.

PS: Maybe we need to encode the raw data before storing it to a file. Ref - https://stackoverflow.com/a/18970406/3955513

Questions regarding the plugin

@tchvu3 thanks for your amazing work here!

I'm about to start a simple journal app where I can record an entry and later on add some more information to that recording.
A few questions:

  • After you stop the recording, where the file is saved? in a temp folder?
  • If yes, can I use the "Filesystem" plugin from Capacitor to create/move this file to a permanent folder that will be created when the user install my app?

Thank you!

Memory leak issue

After introducing "capacitor-voice-recorder" only works with more than 4GB memory, it can only build with 8GB. This occurs even in an example project with only voice-recorder.

<--- Last few GCs --->

[40317:0x104e00000] 920452 ms: Mark-sweep (reduce) 4094.2 (4102.2) -> 4093.8 (4103.5) MB, 44767.0 / 0.0 ms (average mu = 0.214, current mu = 0.001) allocation failure scavenge might not succeed

<--- JS stacktrace --->

FATAL ERROR: MarkCompactCollector: young object promotion failed Allocation failed - JavaScript heap out of memory
1: 0x10130d6e5 node::Abort() (.cold.1) [/usr/local/bin/node]
2: 0x1000b1c49 node::Abort() [/usr/local/bin/node]
3: 0x1000b1daf node::OnFatalError(char const*, char const*) [/usr/local/bin/node]
4: 0x1001f60f7 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
5: 0x1001f6093 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
6: 0x1003a54f5 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/usr/local/bin/node]
7: 0x100400513 v8::internal::EvacuateNewSpaceVisitor::Visit(v8::internal::HeapObject, int) [/usr/local/bin/node]
8: 0x1003e7c7b void v8::internal::LiveObjectVisitor::VisitBlackObjectsNoFail<v8::internal::EvacuateNewSpaceVisitor, v8::internal::MajorNonAtomicMarkingState>(v8::internal::MemoryChunk*, v8::internal::MajorNonAtomicMarkingState*, v8::internal::EvacuateNewSpaceVisitor*, v8::internal::LiveObjectVisitor::IterationMode) [/usr/local/bin/node]
9: 0x1003e78a5 v8::internal::FullEvacuator::RawEvacuatePage(v8::internal::MemoryChunk*, long*) [/usr/local/bin/node]
10: 0x1003e7516 v8::internal::Evacuator::EvacuatePage(v8::internal::MemoryChunk*) [/usr/local/bin/node]
11: 0x100404e1e v8::internal::PageEvacuationTask::RunInParallel(v8::internal::ItemParallelJob::Task::Runner) [/usr/local/bin/node]
12: 0x1003bffc2 v8::internal::ItemParallelJob::Task::RunInternal() [/usr/local/bin/node]
13: 0x1003c042f v8::internal::ItemParallelJob::Run() [/usr/local/bin/node]
14: 0x1003e9509 void v8::internal::MarkCompactCollectorBase::CreateAndExecuteEvacuationTasks<v8::internal::FullEvacuator, v8::internal::MarkCompactCollector>(v8::internal::MarkCompactCollector*, v8::internal::ItemParallelJob*, v8::internal::MigrationObserver*, long) [/usr/local/bin/node]
15: 0x1003e9141 v8::internal::MarkCompactCollector::EvacuatePagesInParallel() [/usr/local/bin/node]
16: 0x1003d5107 v8::internal::MarkCompactCollector::Evacuate() [/usr/local/bin/node]
17: 0x1003d2993 v8::internal::MarkCompactCollector::CollectGarbage() [/usr/local/bin/node]
18: 0x1003a5bbb v8::internal::Heap::MarkCompact() [/usr/local/bin/node]
19: 0x1003a2169 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/local/bin/node]
20: 0x10039ff21 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
21: 0x1003ae7da v8::internal::Heap::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
22: 0x1003ae861 v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
23: 0x10037bf12 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/local/bin/node]
24: 0x1006fc158 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
25: 0x100a893d9 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/usr/local/bin/node]
26: 0x100a63a62 Builtins_MapConstructor [/usr/local/bin/node]
27: 0x100a1e421 Builtins_JSBuiltinsConstructStub [/usr/local/bin/node]
28: 0x2e71f63dcdb5
29: 0x2e71f63d3281
30: 0x2e71f63e0456
31: 0x2e71f63d3235

"recordDataBase64" sometimes contains the mimeType

Hello,

I noticed that:

  • on web recordDataBase64 contains the mimeType and encoding, e.g.: data: audio/webm;codecs=opus; base64, <actual encoding>
  • on mobile it it just the raw encoding

Is that working as intended?

Thanks!

Help/Question: using this plugin to play the recorded audio with capacitor native-audio

Im using the plugin to record and now want to use it with native-audio plugin for playing.
What I found as a problem?
This plugin give base64 string. I found a solution to use it with cordova https://ourcodeworld.com/articles/read/279/how-to-create-an-audio-file-from-a-mp3-base64-string-on-the-device-with-cordova
In this given solution we convert it to blob object which in turn taken by cordova FileSystem plugin to write it to a file.

Although to use it with capacitor we need to convert it to string. Because Filesystem Plugin by capacitor accepts a string as data then encodes it with utf-8, utf-16 or ascii to save it. Which can be consumed by native audio.

Implementation.

import { FilesystemDirectory, FilesystemEncoding, Plugins } from '@capacitor/core';
import { useState } from 'react';
import { GenericResponse, RecordingData } from 'capacitor-voice-recorder';
import { TextAudioFieldOperations } from '@gastromatic/ui-mobile/dist/components/TextAudioField';

const b64toBlob = (b64Data: string, contentType: string, sliceSize= 512): Blob  => {

  const byteCharacters = atob(b64Data);
  const byteArrays = [];

  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);

    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);

    byteArrays.push(byteArray);
  }

  return new Blob(byteArrays, { type: contentType });
}

const useAudio = (): TextAudioFieldOperations => {
  const { VoiceRecorder, NativeAudio, Filesystem } = Plugins;
  const [audio, setAudio] = useState({recordDataBase64: ''});
  const [uri, setURI] = useState('');

  const onStart = ():void => {
    VoiceRecorder.hasAudioRecordingPermission().then((result: GenericResponse) => {
        if(result.value) {
          VoiceRecorder.startRecording();
        } else {
          VoiceRecorder.requestAudioRecordingPermission().then((result: GenericResponse) =>
            result.value && VoiceRecorder.startRecording()
          );
        }
      }
    );
  };

  const onStop = async (): Promise<void> => {
    VoiceRecorder.stopRecording()
      .then((result: RecordingData) => setAudio(result.value));
    const blob = b64toBlob(
      audio.recordDataBase64,
      'audio/aac'
    );
    await Filesystem.writeFile({
      path: 'temp.aac',
      data: await blob.text(),
      directory: FilesystemDirectory.Cache,
      encoding: FilesystemEncoding.UTF8,
    }).then((result)=> setURI(result.uri));
  };

  const onPlay =():void => {
    NativeAudio.preloadComplex({
      assetPath: uri,
      assetId: "im_batman",
      volume: 1.0,
      audioChannelNum: 1,
    });
    NativeAudio.play({
      assetId: "im_batman",
    });
  };

  const onPause = ():void => {
    NativeAudio.pause({
      assetId: "im_batman",
    });
  }

  const onDelete = ():void => {
    setAudio({recordDataBase64: ''});
  };

  return {
    onStart,
    onStop,
    onPlay,
    onPause,
    onDelete,
    onSeek: (): void => console.log('Seek'),
  }
};

export default useAudio;

I'm still getting Error 00x00 by native-audio. Or am I doing something wrong?
How can we achieve this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.