Ah, looking at that title there you probably think, "Surely this is a simple matter in Cordova and surely this is Raymond, once again, blogging something is incredibly obvious and simple just to drive people to his Amazon Wishlist." Yep, that's what I thought too. This weekend I began work on a simple little Cordova app for my son. I thought it would be a great blog post too. But while working on it, I ran into an issue with audio recordings that drove me bat-shit crazy for a few hours, so I thought I'd better share so others don't have to bang their heads against the wall too.
Because this problem turned into a royal cluster-you know what, I've decided to blog it about it in detail here and talk about the app itself later this week. For now, consider this use case:
"You want to allow the user to make an audio recording. Later, the user can play that recording."
Simple, right? So I began working on a form that would let the user make the recording as well as name it. I wanted them to be able to do the recording as well as play it back to ensure they liked it.

Clicking Record fires off a call to the Media Capture plugin:
var captureError = function(e) {
console.log('captureError' ,e);
}
var captureSuccess = function(e) {
console.log('captureSuccess');console.dir(e);
$scope.sound.file = e[0].localURL;
$scope.sound.filePath = e[0].fullPath;
}
$scope.record = function() {
navigator.device.capture.captureAudio(
captureSuccess,captureError,{duration:10});
}
This is boiler-plate Media Capture usage here. I'm storing both the URL and file path in $scope so I can save it later. The Play feature is also pretty trivial:
$scope.play = function() {
if(!$scope.sound.file) {
navigator.notification.alert("Record a sound first.", null, "Error");
return;
}
var media = new Media($scope.sound.file, function(e) {
media.release();
}, function(err) {
console.log("media err", err);
});
media.play();
}
This worked fine in Android and iOS. But I noticed something weird. When I looked at the Media Capture object in captureSuccess, I saw this in Android:

See the portion I called out there? Persistent. Cool. That gives me the warm fuzzies. However, in iOS, I saw this:

As you can see, it is being stored in a temporary location, which is not good. Unfortunately, there is nothing in the Media Capture plugin that you can change to modify this behavior. Therefore - the answer was clear.
The File System!

So in theory, this should be easy. First, we pick a persistent location that covers both Android and iOS. The File plugin provides such an alias: cordova.file.dataDirectory
We have a folder, right? So literally all we need to do is copy a file from one location to another. Copy. A damn. File.
But we've got an issue. First, we have to give the file a unique name. To handle that, I just used time and the existing extension.
var extension = $scope.sound.file.split(".").pop();
var filepart = Date.now();
var filename = filepart + "." + extension;
console.log("new filename is "+filename);
I then spent about an hour trying to get the copy command to work. I began by adding numerous console.log messages with the F word in it. If you don't know what that word is, ask your teenagers. I thought that with a file path, I could just do this:
var file = new FileEntry(some damn path);
But nah, that would be too easy. You need to use window.resolveLocalFileSYstemURL
first. And since the File copy command requires a path, you have to do this twice. Here's the code I ended up with. I removed a few console.log messages that may offend some readers. If you're curious, I went way beyond just saying the F-word.
window.resolveLocalFileSystemURL(loc, function(d) {
window.resolveLocalFileSystemURL($scope.sound.file, function(fe) {
fe.copyTo(d, filename, function(e) {
console.log('success inc opy');
console.dir(e);
$scope.sound.file = e.nativeURL;
$scope.sound.path = e.fullPath;
Sounds.save($scope.sound).then(function() {
$ionicHistory.nextViewOptions({
disableBack: true
});
$state.go("home");
});
}, function(e) {
console.log('error in coipy');console.dir(e);
});
}, function(e) {
console.log("error in inner bullcrap");
console.dir(e);
});
All in all, it isn't that bad. A few nested callbacks, and nearly half my code there is related to my app, not the actual issue, it just took me a while to get there.
Luckily, everything worked perfectly.

So at this point, I've saved the location of my audio file so I can use it in the Media api, but the new location doesn't work in the Media plugin anymore. Why? I don't freaking know. I blogged last year about how when you use the Media plugin and a relative path, you have to do something funky on Android, basically prefix your relative URL. In my case, I'm using a file:// url and I just assumed it would work.
And here is where things got awesome - it worked perfectly in Android but not iOS. Because - reasons. I brought this up on the Cordova Slack channel and @purplecabbage mentioned that a relative path may work. In my dev tools, I tried to manually create a Media object via the console. I discovered that - given the file name - I could access the file with the Media plugin by using this as the root: "../Library/NoCloud/"
.
So now my play code looks like so:
var playSound = function(x) {
getSounds().then(function(sounds) {
var sound = sounds[x];
/*
Ok, so on Android, we just work.
On iOS, we need to rewrite to ../Library/NoCloud/FILE
*/
var mediaUrl = sound.file;
if(device.platform.indexOf("iOS") >= 0) {
mediaUrl = "../Library/NoCloud/" + mediaUrl.split("/").pop();
}
var media = new Media(mediaUrl, function(e) {
media.release();
}, function(err) {
console.log("media err", err);
});
media.play();
});
}
So... yeah. That's it. My app now works. I can record and test audio, and I can persist it to a permanent file system. As I said, I'll share the real app later this week.
Archived Comments
I had some success with this:
https://github.com/mattdiam...
as a pure web application in Chrome.
Although it makes a .wav file available as a link, I'm not sure of it's persistence - you can't just copy/paste the link and expect an incognito window to open it.
I didn't quite understand the file system part of it.
Oh I faced the same issue with a client's app I was building. Man, these non-interoperability between plugins and inconsistencies between the cordova platforms is giving a bad name to hybrid apps. I wish we can somehow standardise the plugins behaviour.
Man the cordova filesystem is a like mystical place for rituals.. I had tutorials about taking images, storing, presenting and maybe sending them out (http://devdactic.com/comple.... You know what? I finally gave in.
If you want to develop apps you don't want to fight with resolveFileUrls, permissions and magic wrong paths due to inconsistency between iOS and Android. I could really go bonkers on this. Completely agree with you and I can understand every F word in your logging rage :D
Thanks for posting this, I'm sure it will save me some headaches in the near future :)
Been there, done that. It's a hell to work with. Same for pictures/galleries or saving canvas to images for upload. And who is gonna solve this? Or..
I have an error that say 'Error: navigator.device is undefined' in
firefox and say 'TypeError: Cannot read property 'capture' of undefined'
in chrome, but why ?
Because you aren't running on a device. This is meant for the device.
It is possible to record audio without going through the native interface of the device ? I did not think infirm solution , I have to implement similar functionality to the whatsapp in a hybrid application with ionic . Would you have any tips ?
If I remember right, the Media plugin, vs Media Capture, will allow for this. Try it.
But I do not want to Get a native interface audio recording application I want to implement my own Power interface.
And as I said, i believe the Media plugin doesn't have any UI. Please test it and confirm. It should take you approximately 10 minutes.
NOT work I followed step by step instructions of ITS and When I click on the button Open and audio recording application to android
Ah, than it may not be possible. You could try getUserMedia.
I used navigator.getUserMedia in navegor worked perfectly , however the phone displays the message that it is not possible acessaro device's microphone . I'm getting desperate my deadline is already over and no solution works properly .
Wish I could help you more, but that's all I got.
is there anyway to define the output file type i mean mp3 or wav
As far as I know, from reading the docs, there is no way to specify the output.
yes, i used other plugin for generate mp4 file with m4a
thank you very much
Which plugin did you use?
audio recorder api
https://www.npmjs.com/packa...
Could you please explain how can I get recorder file? For example I want to upload it to server.
The URL you get points to a local file. You could use the FileTransfer plugin to upload it.
success function return file path
you can use transfer file plugin and pass this path
it's working with me ... let me know if you like to write a sample
Ok, after some digging I faced with new question. Is it possible to capture micro data and transfer it to database as blob object ?and then read it?
What I actually want is, capture audio file and upload it to server. I tried to use it with firebase, but seems firebase will not solve my needs.
i used File Transfer and code as attachment image
https://www.npmjs.com/packa...
also i used ASP.net and read files from http files with key in code
[options.fileKey = "uploadFile";]
its read as file stream and i convert it to byte as well to save to database
------------
sorry for bad English :)
i used ASP.net MVC and the file received in files request as below
HttpPostedFile MyFile = HttpContext.Current.Request.Files["recFile"];
//MyFile.InputStream;
Did you have any issue with the recording and playback volume being too low with cordova-plugin-audio-recorder?
hey can you please share the complete code and along with the library or scripts required.. i am a newbie please..
It is here: http://www.raymondcamden.co...
Hey thanx,
But i want to make an app where i have a video and i can record the audio so i cant work with CaptureAudio i need the Media plugin example where the audio is recorded in my app itself without giving control to the OS specific audio recording app.
Do you have something for that ?
Check the cordovedemos repo I have - I've put most of my demos there.
hi, I was Copy paste all your code from github but i'm found an error (see picture) sorry if my question so easily to fix but I'm newbie so I don't know what should I do to fix that error , thank you so much if you want to help me :)
Hmm - thats definitely a bug. You can try removing the $scope.apply call.
when i click record nothing happens(android) !!! im new to ionic it will be great help for me if u guide me !!
do we need to install any cordova plugins ????
Please use Chrome Remote Debugging and tell me what you see in the console.
Hello! I don't get any errors in the console in Chrome Remote Debugging, however my app doesn't work properly. When I run the app I see this. How can I fix it? I will really appreciate it if you can help me.
Thank you in advance
So the button to add a new sound isn't showing up. In the Network panel, do you see errors there?
No, no errors there either
Wait - what code are you using the app?
From the Cordova-Examples repo on Github
Ok, so the button only shows when the Cordova deviceready event is fired. I assume you are building this out as a real app, right?
yes
Then it may be something in this area:
$scope.cordova = {loaded:false};
$ionicPlatform.ready(function() {
$scope.$apply(function() {
$scope.cordova.loaded = true;
});
});
Add a console.log after loaded is set to true and confirm it shows up.
Initially I got this error :
Error: [$rootScope:inprog] $digest already in progress
and I removed the $scope.$apply call as you suggested below.
I added it back again now, the html is shown properly and I get the same error again
Hmpth, that thing. So w/o scope apply it works or does nothing?
Doesn't do anything without it, only shows the screen I originally posted
Then for now, remove the show logic in html so its always there - and just be careful to not click *immediately*. That will get you past that for now. That's all I can suggest.
Thanks a lot for your help
recorded sounds dont play. error in console is ReferenceError: Media is not defined. i am a newbie please help me fix it
This means you probably didn't add the Media plugin.
I have the plugin
Ok, are you waiting for deviceready to fire? In Ionic this can be done with IonicPlatform.ready.
yes, but i have modified your code like this
.run(function($ionicPlatform, $rootScope) {
$rootScope.loaded = false;
$ionicPlatform.ready(function() {
$rootScope.loaded = true;
......
because $scope.apply was causing some problems
Then I'd need to dig deeper into your code to figure it out. At that point it has to be paid engagement - sorry.
when I press play button, I can't hear in phone 6, can you please suggest. file showing save. but can't play.
Check w/ Safari Remote Debug and see if there is an error in the console.
look the plugin version, it works like this until version 2.1.0
https://issues.apache.org/j...
Hello all I am sending $scope.sound.filePath file to server .3gpp and php convert to wav and save in data base, but when you play server file not playing. please help.
Do you see an error in console?
var captureSuccess = function(e) {
console.log('captureSuccess');console.dir(e);
$scope.sound.file = e[0].localURL;
$scope.sound.filePath = e[0].fullPath;
$scope.audioFile = $scope.sound.file;
}
I am sent the file path ($scope.audioFile ), now it store. but when php developer change the ext.3gpp to .wav. its not play. is it right way?
I have not use cordova FileTransfer. is it needed to send $scope.sound.filePath(audio) to server?
Well, not required, but recommended.
This is not what I'm asking. I think we need to back up a bit.
You said you are sending an audio file to your PHP server. It does a conversion, and you want to play the converted audio.
Step one is for you to confirm that your file is being uploaded. I do not use PHP, but look at your PHP server code and add some logging to see if the file comes in. Also, actually look for the converted file on your PHP server.
Sorry , I was wrong, file not uploaded.
Can you please help me after recording audio file, how can I send the file to the server. please give me a example code. now I am sending the "$scope.sound.filePath" but can't send anything.
Look into the FileTransfer docs. Since this isn't really on topic for this blog post, if you have issues with FileTransfer, please post your question to StackOverflow.
media.stop not working
If you don't see an error in the console, and you can create a reproducible case, then your best option is to file a bug report.
Hi, can you share this project?
The code may be found on this blog post: https://www.raymondcamden.c...
As I use navigator.device.capture.captureAudio it always go to error callback saying error code 3 with message cancelled.
Are you using my code exactly or did you modify it? What device and os version?
I'm using your code exactly. I'm using it on moto x play with Android 6
Ok, check code 3 and see which of the constants it is.
hi Raymond, what would you charge to solve an issue in my code very similar to this one?
regards
Not quite sure what you are asking.
Hello Raymond, thank you for sharing that app, it is very insightful and works perfectly!
I'm having a couple issues though
1. you know any way to increase recording volume? I tested on iphone 5s and moto G, the volume is quite low on both devices.
2. Also, I understand the microphone image is provided by the media-capture plugin, so, is not possible at all to override it?
best regards
As far as I know, you don't have control over either.
The Boromir pic made me laugh loudly .... then cry :D
Hello Raymond,
How can I delete origin file after recording? is it possible to delete after uploading the record file and delete below the full path file.
Here is the original path:
fullPath: "file:/storage/emulated/0/Sounds/Voice%20004.m4a"
Thank you.
Yes. Use the File system plugin. There is an API to delete.
Hello Raymond, thank you for the app and the book. I tried the examples, mysoundboard isn't saving the file in Marshmallow.
fe.copyTo(d, filename, function(e) {
fails with code 1. The cordova-file-plugin isn't working in marshmalow 6.0. Could you please help?
Can you try to get more information about the error? Ie ore than the code.
Hello, In $scope.saveSound, the $scope.sound.file is "file:///storage/emulated/0/Voice%20Recorder/Voice%20046.m4a"
when it hits the fe.copyTo it goes to error message and I get code 1. Sorry I'm just beginning and I couldn't get much out of it yet. I'm working with console messages. I put some debug messages and found out that fe.copyTo is the issue in Android 6. Android 5 below it just works fine.
window.resolveLocalFileSystemURL(loc, function(d) {
window.resolveLocalFileSystemURL($scope.sound.file, function(fe) {
fe.copyTo(d, filename, function(e) {
Thanks,
Karthik.
Hmm - maybe its an issue with the simulator? (Are you using it? Maybe try a real device.)
Hello, no, it is the real device Samsung s6 edge running Android 6.0.1. Wondering whether the cordova file plugin having issues with Marshmallow to access Voice Recorder files. Digging the internet but not finding much. Cordova version 6.3.0 and cordova-plugin-file is 4.2.0.
And yes, lower Android versions this works, both emulator and real. I also tested with iOS 8 and 9 and they are working like champions there as well! :)
You got me here. I've got a modern Android device - but won't have access to it till next week. Will try to test then. Sorry!
Thank you Raymond appreciate it. Also, congratulations on the book, it is very readable and I'm following it word by word. Will post back if I find anything with Android versions.
Hello Raymond, I am using samsung tab, tab has no mic to record or call option, when I trying to record project has crashed. Is it work with out mic in any devices? please help
Well it can't work without a way to record audio, but it shouldn't crash. If you remote debug, can you see an error in console?
hello sir, Is there any way to call record automatically during phone call?
Not that I know of. That's not really on topic for this blog post. I'd check StackOverflow.
Hello Raymond Do you know if is possible detected de file media if stop recording or cellphone turn off?
thanks.
Well you know when recording is done, that's when the success handler ran. If the device is turned off, how do you expect any code to run?
Well do not explain me correctly, the situation is to save the file in a web servidor if by an external agent stop the recording, I hope is possible rescue the file media, thanks for responses
Well to be clear, recording will ALWAYS stop. You can't record forever. But as for uploading, look at the FileTransfer plugin.
Hola gracias por compartir, queria saber si puedes explicar como hacerlo en Ionic 2
No hablo espanol. Or Spanish.
Hello thanks for sharing, I wanted to know if you can explain how to do it in Ionic 2
Well, the main issues would be the same. What would change is, obviously, the framework. I'm updating some of my Ionic V1 posts for V2, but I can't do them all - it would take forever. ;)
Hi Raymond, is there a way to record the audio without the device launching the record audio file? I want to implement something like whatsapp does, push a button for recording and realease it for stopping from recording, all inside my app
Thanks in advance
I believe the Media plugin, *not* Media Capture, allows this.
hi i try your code exactly but am getting this error and also am using media capture plugin.
capture Error No Activity found to handle Intent { act=android.provider.MediaStore.RECORD SOUND }
could you please hepl me regarding this
The first result I got when Googling for this sounds like it could be it: https://stackoverflow.com/q...
Hi, I have implementing a same way to record audio file but getting problem. Can you please give me some hint. Here is my stackoverflow post: https://stackoverflow.com/q...
Sorry I don't have much to suggest there.
Hi Raymond,
Thanks for the great post, you probably saved me days of trying to work this all out so much appreciated.
Everything is working for me except the file will not play (on IOS, haven't tried android as yet) I don't get an error and to see if everything is working I played a publicly accessible remote wav file and it works.
I added a media status call back to the media object and when playing the remote file I get the following status codes:
2 (MEDIA_RUNNING)
4 (MEDIA_STOPPED)
Then the media success callback fires and we release the media object "playAudio():Audio Success" all good!
When I try play my file I get:
2 (MEDIA_RUNNING)
And thats it, it never stops, I dont hear anything and when change the volume buttons the phone indicates volume change instead of ringer volume (when no media is being played) so it's like its trying to play it.
Do you have any idea what could be wrong?
Thanks!
To be clear, are you saying the public file worked but not a local file?
Yeah exactly, so I record the file. Copy from temp directory to the perminent directory and everything seems to be fine, then when I load the file with media using the relative ../library/nocloud. Etc it seems to find it but not play it. If I give a bogus name it fails with an error but trying to play the real file it appears to be trying to play it but I get no feedback.
I'm not sure. If you see no errors in console, maybe try running via XCode and see if something more is displayed there.
Hey - see the comments here (https://issues.apache.org/j.... It may apply.
Just wanted to say thanks. Excellent article. I can easily guess at the code comments removed. I recently implemented the very basics of the media plugin using OutSystems. I used all the comments you removed... plus a few more. OutSystems uses a wrapper around the Javascript code needed to talk to plugins. Doing this isolates all calls. Your article was written 2 years ago and my impression, after working with OutSystems, is that nothing ever changes. It's always tough to get stuff done.
Thanks for the injected humour. Cheers.
You are most welcome.
Is there a resource for doing this on Ionic 3?
Fernando, did you ever figure out how to do this on Ionic 2?
Not from me, no.
To be clear, the UI and app itself changes, the use of the plugins really doesn't. That's the difficult part. You understand that, right?
hi sir,
i cant proceed to audio recording because i have an error [ " ReferenceError: Can't find variable:$scope "]. can you help me
Are you using an Ionic/Angular application?
How did you get value of "loc" in "window.resolveLocalFileSystemURL(loc, function(d) ".
I am having some trouble storing audio file in persistent storage.
Please suggest some solution to my problem posted https://stackoverflow.com/q...
I honestly don't remember. I can say this is the final version of the app: https://github.com/cfjedima...
Hi Raymond
Why not just use the voice recorder in android or ios. It can save the files and share via email, sms, etc.
It's been a few years, but I believe the path I took gave me more control over the UI versus opening up another app.