Issue in using nativescript-audio plug-in


#1

Hi I am trying following code and running this code in android(sdk>23) emulator. Console is showing all permissions set to true and TNSRecorder.CAN_RECORD() also true but its failing at “this.recorder.start( recorderOptions )”:

 public start( ) {
// alert( 'start().' );
  console.log("1cPermission REQUESTED is",              android.Manifest.permission.RECORD_AUDIO);
      const permPromise =   permissions.requestPermissions([
                           android.Manifest.permission.RECORD_AUDIO,
                           //android.Manifest.permission.CAPTURE_AUDIO_OUTPUT,// does not get this permission
                           android.Manifest.permission.ACCESS_FINE_LOCATION,
                           //android.Manifest.permission.STORAGE,// didn't work
                           android.Manifest.permission.READ_EXTERNAL_STORAGE,
                           android.Manifest.permission.WRITE_EXTERNAL_STORAGE],
           "I really need all the permissions in the world!");
           //const permPromise = permissions.requestPermission(     android.Manifest.permission.RECORD_AUDIO, "Let me hear your thoughts..." );
     permPromise.then((res)=>{
    console.log("audio.TNSRecorder.CAN_RECORD()", audio.TNSRecorder.CAN_RECORD());
    // you should check if the device has recording capabilities
    if ( audio.TNSRecorder.CAN_RECORD() ) {
        console.log("3 Response res is", JSON.stringify(res));
        this.recorder = new audio.TNSRecorder();

        var audioFolder = fs.knownFolders.currentApp().getFolder( "audio" );
        console.log("4 Folder is", JSON.stringify(fs.knownFolders.currentApp()));

        var recorderOptions = {

            filename: audioFolder.path + '/recording.mp3',                        
            infoCallback: function() {
                console.log( 'infoCallback' );
            },
            errorCallback: function() {
                console.log( 'errorCallback' );
                alert( 'Error recording.' );
            }
        };
        console.log("4 Folder Path is",audioFolder.path);

        console.log( 'RECORDER OPTIONS: ' + JSON.stringify(recorderOptions) );
        console.log("5 Five is");
        const recPromise = this.recorder.start( recorderOptions );
        recPromise.then((res) => {
                data.set( 'isRecording', true );
                console.log("6 Six is");
            });
        recPromise.catch((err) => {
                data.set( 'isRecording', false );
                console.log("7 Seven is");
                console.log( 'ERROR: ' + err );                                      
            });

    } else {
        alert( 'This device cannot record audio.' );
    }

});
permPromise.catch( (err) => {
    console.log( "Uh oh, no permissions - plan B time!" );
    });
}

I am getting following error

java.lang.RuntimeException: start failed. JS: android.media.MediaRecorder.start(Native Method)

Please let me know how I can fix this issue? Thanks in advance.


#2

Can you show the entire code here? Specifically your imports/requires would be helpful.


#3

Thanks Brad for reply. Here is code:

    import { Component } from "@angular/core";
    import * as application from "application";
    import { TNSRecorder, TNSPlayer, AudioPlayerOptions, AudioRecorderOptions } from 'nativescript-audio';
    
    import pageModule = require("ui/page");
    var observable = require( "data/observable" );
    var fs = require( 'file-system' );
    var audio = require( "nativescript-audio" );
    import * as permissions from "nativescript-permissions";
    declare var android;
    
    var data = new observable.Observable( {});
    
    
    @Component( {
        selector: "my-app",
        template: `
        <StackLayout>
            <ActivityIndicator color="#3489db" busy="{{ isRecording }}"> </ActivityIndicator>
            <Button text="Start Recording" (tap)="start()"> </Button>
            <Button text="Stop Recording" (tap)="stop()"> </Button>
            <Button text="Get Recorded File" (tap)="getFile()"> </Button>
            <label text="{{ recordedAudioFile }}" color="#3489db" textWrap="true"></label>
        </StackLayout>
      `,
        styles: [`
        @keyframes spin {
          from { transform: rotate(0); } to { transform: rotate(360); }
        }
        Image {
          animation-name: spin; animation-duration: 3s;
          animation-iteration-count: infinite;
          animation-timing-function: linear;
        }
      `]
    })
    
    export class AppComponent {
    
        recorder: any;
        constructor(private page: pageModule.Page) {
            page.bindingContext = data;
            data.set( 'isRecording', false );
        }
    
    /*    public onNavigatingTo( args ) {
            var page = args.object;
            page.bindingContext = data;
    
            data.set( 'isRecording', false );
        }*/
    
        /* START RECORDING */
    
        public start( ) {
           // alert( 'start().' );
            console.log("1cPermission REQUESTED is", android.Manifest.permission.RECORD_AUDIO);
            const permPromise =   permissions.requestPermissions([
                                           android.Manifest.permission.RECORD_AUDIO,
                                           //android.Manifest.permission.CAPTURE_AUDIO_OUTPUT,// does not get this permission
                                           android.Manifest.permission.ACCESS_FINE_LOCATION,
                                           //android.Manifest.permission.STORAGE,// didn't work
                                           android.Manifest.permission.READ_EXTERNAL_STORAGE,
                                           android.Manifest.permission.WRITE_EXTERNAL_STORAGE],
                           "I really need all the permissions in the world!");
                //const permPromise = permissions.requestPermission( android.Manifest.permission.RECORD_AUDIO, "Let me hear your thoughts..." );
                permPromise.then((res)=>{
                    console.log("audio.TNSRecorder.CAN_RECORD()", audio.TNSRecorder.CAN_RECORD());
                    // you should check if the device has recording capabilities
                    if ( audio.TNSRecorder.CAN_RECORD() ) {
                        console.log("3response res is", JSON.stringify(res));
                        this.recorder = new audio.TNSRecorder();
    
                        var audioFolder = fs.knownFolders.currentApp().getFolder( "audio" );
                        console.log("4Folder is", JSON.stringify(fs.knownFolders.currentApp()));
    
                        var recorderOptions = {
    
                            filename: audioFolder.path + '/recording.mp3',                        
                            infoCallback: function() {
                                console.log( 'infoCallback' );
                            },
                            errorCallback: function() {
                                console.log( 'errorCallback' );
                                alert( 'Error recording.' );
                            }
                        };
                        console.log("4Permission REQUESTED is",audioFolder.path);
    
                        console.log( 'RECORDER OPTIONS: ' + JSON.stringify(recorderOptions) );
                        console.log("5 Five is");
                        const recPromise = this.recorder.start( recorderOptions );
                        recPromise.then((res) => {
                                data.set( 'isRecording', true );
                                console.log("6 Six is");
                            });
                        recPromise.catch((err) => {
                                data.set( 'isRecording', false );
                                console.log("7 Seven is");
                                console.log( 'ERROR: ' + err );                                      
                            });
    
                    } else {
                        alert( 'This device cannot record audio.' );
                    }
    
                });
                permPromise.catch( (err) => {
                    console.log( "Uh oh, no permissions - plan B time!" );
                });
        }
    
        /* STOP RECORDING */
    
        public stop(  ) {
            alert( 'stop().' );
            if ( this.recorder != undefined ) {
                this.recorder.stop().then( function() {
                    data.set( 'isRecording', false );
                    alert( 'Audio Recorded Successfully.' );
                }, function( err ) {
                    console.log( err );
                    data.set( 'isRecording', false );
                });
            }
        }
    
        public getFile(  ) {
            alert( 'getFile().' );
            try {
                var audioFolder = fs.knownFolders.currentApp().getFolder( "audio" );
                var recordedFile = audioFolder.getFile( 'recording.mp3' );
                data.set( "recordedAudioFile", recordedFile.path );
            } catch ( ex ) {
                console.log( ex );
            }
        }
    }

#4

No need to import the audio module and then require it. You can import what
you need with the import statement and drop the audio require you have.
Then remove the code where you call the audio require instance. Then let’s
see what happens.


#5

I’d also change this:

  const recPromise = this.recorder.start( recorderOptions );
                        recPromise.then((res) => {
                                data.set( 'isRecording', true );
                                console.log("6 Six is");
                            });
                        recPromise.catch((err) => {
                                data.set( 'isRecording', false );
                                console.log("7 Seven is");
                                console.log( 'ERROR: ' + err );                                      
                            });

by dropping the const value - you don’t need that. Just call

this.recorder.start(yourOptions).then((result) => {
  // do something
 }, (err) => { 
    // oh crap! 
});

Also might need more of the error message you are getting. Just drop it here in a snippet.


#6

Brad,
Below is the code after cleaning:

    import { Component } from "@angular/core";
    import { TNSRecorder, AudioRecorderOptions } from 'nativescript-audio';
    import { knownFolders, Folder, File } from 'file-system';   
    import { Page } from 'ui/page'; 
    import { Observable } from 'data/observable'; 
    import * as permissions from "nativescript-permissions";
    
    declare var android;

    @Component( {
        selector: "my-app",
        template: `
        <StackLayout>
            <ActivityIndicator color="#3489db" busy="{{ isRecording }}"> </ActivityIndicator>
            <Button text="Start Recording" (tap)="start()"> </Button>
            <Button text="Stop Recording" (tap)="stop()"> </Button>
            <Button text="Get Recorded File" (tap)="getFile()"> </Button>
            <label text="{{ recordedAudioFile }}" color="#3489db" textWrap="true"></label>
        </StackLayout>
      `            
    })
    
    export class AppComponent {
    
        recorder: TNSRecorder;
        data = new Observable( {});
        constructor(private page: Page) {
            page.bindingContext = this.data;
            this.data.set( 'isRecording', false );
        }
            
        /* START RECORDING */
    
        public start( ) {
           // alert( 'start().' );
            console.log("1.Permission REQUESTED is", android.Manifest.permission.RECORD_AUDIO);
            const permPromise =   permissions.requestPermissions([
                                           android.Manifest.permission.RECORD_AUDIO,
                                           android.Manifest.permission.ACCESS_FINE_LOCATION,
                                           android.Manifest.permission.READ_EXTERNAL_STORAGE,
                                           android.Manifest.permission.WRITE_EXTERNAL_STORAGE],
                           "I really need all the permissions in the world!");
                //const permPromise = permissions.requestPermission( android.Manifest.permission.RECORD_AUDIO, "Let me hear your thoughts..." );
                permPromise.then((res)=>{
                    console.log("2.audio.TNSRecorder.CAN_RECORD()", TNSRecorder.CAN_RECORD());
                    // you should check if the device has recording capabilities
                    if ( TNSRecorder.CAN_RECORD() ) {
                        console.log("3.response res is", JSON.stringify(res));
                        this.recorder = new TNSRecorder();
    
                        let audioFolder: Folder = knownFolders.currentApp().getFolder( "audio" );
                        console.log("4.Folder is", JSON.stringify(knownFolders.currentApp()));
    
                        let recorderOptions: AudioRecorderOptions = {
    
                            filename: audioFolder.path + '/recording.mp3',                        
                            infoCallback: function() {
                                console.log( 'infoCallback' );
                            },
                            errorCallback: function() {
                                console.log( 'errorCallback' );
                                alert( 'Error recording.' );
                            }
                        };
                        console.log("4a.Permission REQUESTED is",audioFolder.path);
    
                        console.log( 'RECORDER OPTIONS: ' + JSON.stringify(recorderOptions) );
                        console.log("5. Five is");
                        this.recorder.start(recorderOptions).then((result) => {
                            this.data.set( 'isRecording', true );
                            console.log("6. Six is");
                           }, (err) => { 
                               this.data.set( 'isRecording', false );
                               console.log( 'ERROR: ' + err );  
                               console.log("7. Seven is");
                          });        
                    } else {
                        alert( 'This device cannot record audio.' );
                    }
    
                });
                permPromise.catch( (err) => {
                    console.log( "Uh oh, no permissions - plan B time!" );
                });
        }
    
        /* STOP RECORDING */
    
        public stop(  ) {
            alert( 'stop().' );
            if ( this.recorder != undefined ) {
                this.recorder.stop().then( function() {
                    this.data.set( 'isRecording', false );
                    alert( 'Audio Recorded Successfully.' );
                }, function( err ) {
                    console.log( err );
                    this.data.set( 'isRecording', false );
                });
            }
        }
    
        public getFile(  ) {
            alert( 'getFile().' );
            try {
                let audioFolder: Folder = knownFolders.currentApp().getFolder( "audio" );
                let recordedFile: File = audioFolder.getFile( 'recording.mp3' );
                this.data.set( "recordedAudioFile", recordedFile.path );
            } catch ( ex ) {
                console.log( ex );
            }
        }
    }

And below is console output:

Transferring project files…
Successfully transferred app.component.js.
Refreshing application…
Successfully synced application org.nativescript.TApp on device emulator-5554.

JS: Angular is running in the development mode. Call enableProdMode() to enable the production mode.
JS: 1.Permission REQUESTED is android.permission.RECORD_AUDIO
JS: 2.audio.TNSRecorder.CAN_RECORD() true
JS: 3.response res is {“android.permission.RECORD_AUDIO”:true,“android.permission.ACCESS_FINE_LOCATION”:true,“android.permission.READ_EXTERNAL_STORAGE”:true,“android.permission.WRITE_EXTERNAL_STORAGE”:true}
JS: 4.Folder is {"_path":"/data/data/org.nativescript.TalkingBird/files/app","_isKnown":true}
JS: 4a.Permission REQUESTED is /data/data/org.nativescript.TalkingBird/files/app/audio
JS: RECORDER OPTIONS: {“filename”:"/data/data/org.nativescript.TalkingBird/files/app/audio/recording.mp3"}
JS: 5. Five is
JS: ERROR: Error: java.lang.RuntimeException: start failed.
JS: android.media.MediaRecorder.start(Native Method)
JS: com.tns.Runtime.callJSMethodNative(Native Method)
JS: com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1197)
JS: com.tns.Runtime.callJSMethodImpl(Runtime.java:1061)
JS: com.tns.Runtime.callJSMethod(Runtime.java:1047)
JS: com.tns.Runtime.callJSMethod(Runtime.java:1028)
JS: com.tns.Runtime.callJSMethod(Runtime.java:1018)
JS: com.tns.NativeScriptActivity.onRequestPermissionsResult(android.app.Activity.java)
JS: android.app.Activity.dispatchRequestPermissionsResult(Activity.java:7084)
JS: android.app.Activity.dispatchActivityResult(Activity.java:6936)
JS: android.app.ActivityThread.deliverResults(ActivityThread.java:4085)
JS: android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
JS: android.app.ActivityThread.-wrap20(ActivityThread.java)
JS: android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
JS: android.os.Handler.dispatchMessage(Handler.java:102)
JS: android.os.Looper.loop(Looper.java:154)
JS: android.app.ActivityThread.main(ActivityThread.java:6119)
JS: java.lang.reflect.Method.invoke(Native Method)
JS: com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
JS: com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
JS: 7. Seven is

Thanks.


#7

Based on the error and the reference to the start method something isn’t right on what’s being passed to the method. I’m going to guess it’s the filename value that might be the issue. I ran the demo fine the other day. If I have time I’ll try again to make sure everything is okay but I know it was working about a week ago just fine.

Do you have an audio folder in the app root? Also see how we do this in the demo:


  private platformExtension() {
    //'mp3'
    return `${app.android ? 'm4a' : 'caf'}`;
  }

On android try setting your file extension in the filename to m4a instead of mp3 and make sure you have anaudio` folder in the root of your app like the demo here: https://github.com/bradmartin/nativescript-audio/tree/master/demo/app


#8

I tried all these option including hard coding directory and creating folders ects. But no success. I am using emulator(android/windows10/ Lenovo T410 laptop). Not sure where is the issue. I don;t have android phone so can’t try on physical device. It may work there. Also I am new to mobile application development.


#9

I’ll run the demo again tomorrow on an emulator and my phone to make sure
everything is okay. No worries on the issues, just wish you didn’t
encounter it. Can be frustrating but also learning experience :slight_smile: