Repository: cordova-plugin-media Updated Branches: refs/heads/master e5b663ac9 -> 3706b7576
CB-9487: Support getting amplitude for recording Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/3706b757 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/3706b757 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/3706b757 Branch: refs/heads/master Commit: 3706b757679d1470cb7a02a09c92e6faf52687b9 Parents: e5b663a Author: Simon MacDonald <simon.macdon...@gmail.com> Authored: Sun Mar 27 17:54:20 2016 -0400 Committer: Simon MacDonald <simon.macdon...@gmail.com> Committed: Sun Mar 27 18:36:05 2016 -0400 ---------------------------------------------------------------------- README.md | 43 ++++++++++++++++++++++++++ src/android/AudioHandler.java | 17 +++++++++- src/android/AudioPlayer.java | 25 +++++++++++++-- src/ios/CDVSound.h | 3 +- src/ios/CDVSound.m | 63 ++++++++++++++++++++++++++++++-------- tests/tests.js | 31 +++++++++++-------- www/Media.js | 10 +++++- 7 files changed, 162 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index a641e39..6ea4956 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ The following constants are reported as the only parameter to the ### Methods +- `media.getCurrentAmplitude`: Returns the current position within an audio file. + - `media.getCurrentPosition`: Returns the current position within an audio file. - `media.getDuration`: Returns the duration of an audio file. @@ -120,6 +122,47 @@ The following constants are reported as the only parameter to the - __duration__: The duration of the media, in seconds. +## media.getCurrentAmplitude + +Returns the current amplitude of the current recording. + + media.getCurrentAmplitude(mediaSuccess, [mediaError]); + +### Supported Platforms + +- Android +- iOS + +### Parameters + +- __mediaSuccess__: The callback that is passed the current amplitude (0.0 - 1.0). + +- __mediaError__: (Optional) The callback to execute if an error occurs. + +### Quick Example + + // Audio player + // + var my_media = new Media(src, onSuccess, onError); + + // Record audio + my_media.startRecord(); + + mediaTimer = setInterval(function () { + // get media amplitude + my_media.getCurrentAmplitude( + // success callback + function (amp) { + console.log(amp + "%"); + }, + // error callback + function (e) { + console.log("Error getting amp=" + e); + } + ); + }, 1000); + + ## media.getCurrentPosition Returns the current position within an audio file. Also updates the `Media` object's `position` parameter. http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/src/android/AudioHandler.java ---------------------------------------------------------------------- diff --git a/src/android/AudioHandler.java b/src/android/AudioHandler.java index 2ff601d..75cdcde 100644 --- a/src/android/AudioHandler.java +++ b/src/android/AudioHandler.java @@ -164,6 +164,10 @@ public class AudioHandler extends CordovaPlugin { else if (action.equals("messageChannel")) { messageChannel = callbackContext; return true; + } else if (action.equals("getCurrentAmplitudeAudio")) { + float f = this.getCurrentAmplitudeAudio(args.getString(0)); + callbackContext.sendPluginResult(new PluginResult(status, f)); + return true; } else { // Unrecognized action. return false; @@ -471,5 +475,16 @@ public class AudioHandler extends CordovaPlugin { } - + /** + * Get current amplitude of recording. + * @param id The id of the audio player + * @return amplitude + */ + public float getCurrentAmplitudeAudio(String id) { + AudioPlayer audio = this.players.get(id); + if (audio != null) { + return (audio.getCurrentAmplitude()); + } + return 0; + } } http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/src/android/AudioPlayer.java ---------------------------------------------------------------------- diff --git a/src/android/AudioPlayer.java b/src/android/AudioPlayer.java index ede9330..26248e5 100644 --- a/src/android/AudioPlayer.java +++ b/src/android/AudioPlayer.java @@ -501,13 +501,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On sendErrorStatus(MEDIA_ERR_ABORTED); } return false;//we´re not ready yet - } + } else { //reset the audio file player.seekTo(0); player.pause(); - return true; - } + return true; + } } else { //reset the player this.player.reset(); @@ -598,4 +598,23 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.handler.sendEventMessage("status", statusDetails); } + + /** + * Get current amplitude of recording. + * + * @return amplitude or 0 if not recording + */ + public float getCurrentAmplitude() { + if (this.recorder != null) { + try{ + if (this.state == STATE.MEDIA_RUNNING) { + return (float) this.recorder.getMaxAmplitude() / 32762; + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + return 0; + } } http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/src/ios/CDVSound.h ---------------------------------------------------------------------- diff --git a/src/ios/CDVSound.h b/src/ios/CDVSound.h index 90da62e..5e795cc 100644 --- a/src/ios/CDVSound.h +++ b/src/ios/CDVSound.h @@ -110,8 +110,9 @@ typedef NSUInteger CDVMediaMsg; - (void)startRecordingAudio:(CDVInvokedUrlCommand*)command; - (void)stopRecordingAudio:(CDVInvokedUrlCommand*)command; +- (void)getCurrentAmplitudeAudio:(CDVInvokedUrlCommand*)command; - (void)setVolume:(CDVInvokedUrlCommand*)command; - (void)setRate:(CDVInvokedUrlCommand*)command; -@end \ No newline at end of file +@end http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/src/ios/CDVSound.m ---------------------------------------------------------------------- diff --git a/src/ios/CDVSound.m b/src/ios/CDVSound.m index 640ebcd..d99f181 100644 --- a/src/ios/CDVSound.m +++ b/src/ios/CDVSound.m @@ -206,7 +206,7 @@ [errorDict setObject:[NSNumber numberWithUnsignedInteger:code] forKey:@"code"]; [errorDict setObject:message ? message:@"" forKey:@"message"]; - + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:errorDict options:0 error:nil]; return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; } @@ -290,7 +290,7 @@ float customRate = [rate floatValue]; [avPlayer setRate:customRate]; } - + [[self soundCache] setObject:audioFile forKey:mediaId]; } } @@ -420,7 +420,7 @@ if ([resourceURL isFileURL]) { audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:resourceURL error:&playerError]; } else { - /* + /* NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:resourceURL]; NSString* userAgent = [self.commandDelegate userAgent]; if (userAgent) { @@ -541,10 +541,10 @@ } else if (avPlayer != nil) { int32_t timeScale = avPlayer.currentItem.asset.duration.timescale; CMTime timeToSeek = CMTimeMakeWithSeconds(posInSeconds, timeScale); - + BOOL isPlaying = (avPlayer.rate > 0 && !avPlayer.error); BOOL isReadyToSeek = (avPlayer.status == AVPlayerStatusReadyToPlay) && (avPlayer.currentItem.status == AVPlayerItemStatusReadyToPlay); - + // CB-10535: // When dealing with remote files, we can get into a situation where we start playing before AVPlayer has had the time to buffer the file to be played. // To avoid the app crashing in such a situation, we only seek if both the player and the player item are ready to play. If not ready, we send an error back to JS land. @@ -613,7 +613,7 @@ } CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:position]; - + NSString* jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_POSITION, position]; [self.commandDelegate evalJs:jsString]; [self.commandDelegate sendPluginResult:result callbackId:callbackId]; @@ -636,7 +636,7 @@ void (^startRecording)(void) = ^{ NSError* __autoreleasing error = nil; - + if (audioFile.recorder != nil) { [audioFile.recorder stop]; audioFile.recorder = nil; @@ -656,14 +656,20 @@ return; } } - + // create a new recorder for each start record + NSDictionary *audioSettings = @{AVFormatIDKey: @(kAudioFormatMPEG4AAC), + AVSampleRateKey: @(44100), + AVNumberOfChannelsKey: @(1), + AVEncoderAudioQualityKey: @(AVAudioQualityMedium) + }; audioFile.recorder = [[CDVAudioRecorder alloc] initWithURL:audioFile.resourceURL settings:nil error:&error]; - + bool recordingSuccess = NO; if (error == nil) { audioFile.recorder.delegate = weakSelf; audioFile.recorder.mediaId = mediaId; + audioFile.recorder.meteringEnabled = YES; recordingSuccess = [audioFile.recorder record]; if (recordingSuccess) { NSLog(@"Started recording audio sample '%@'", audioFile.resourcePath); @@ -671,7 +677,7 @@ [weakSelf.commandDelegate evalJs:jsString]; } } - + if ((error != nil) || (recordingSuccess == NO)) { if (error != nil) { errorMsg = [NSString stringWithFormat:@"Failed to initialize AVAudioRecorder: %@\n", [error localizedFailureReason]]; @@ -686,7 +692,7 @@ [weakSelf.commandDelegate evalJs:jsString]; } }; - + SEL rrpSel = NSSelectorFromString(@"requestRecordPermission:"); if ([self hasAudioSession] && [self.avSession respondsToSelector:rrpSel]) { @@ -710,7 +716,7 @@ } else { startRecording(); } - + } else { // file did not validate NSString* errorMsg = [NSString stringWithFormat:@"Could not record audio at '%@'", audioFile.resourcePath]; @@ -831,6 +837,39 @@ [[self soundCache] removeAllObjects]; } +- (void)getCurrentAmplitudeAudio:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSString* mediaId = [command argumentAtIndex:0]; + +#pragma unused(mediaId) + CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId]; + float amplitude = 0; // The linear 0.0 .. 1.0 value + + if ((audioFile != nil) && (audioFile.recorder != nil) && [audioFile.recorder isRecording]) { + [audioFile.recorder updateMeters]; + float minDecibels = -60.0f; // Or use -60dB, which I measured in a silent room. + float decibels = [audioFile.recorder averagePowerForChannel:0]; + if (decibels < minDecibels) { + amplitude = 0.0f; + } else if (decibels >= 0.0f) { + amplitude = 1.0f; + } else { + float root = 2.0f; + float minAmp = powf(10.0f, 0.05f * minDecibels); + float inverseAmpRange = 1.0f / (1.0f - minAmp); + float amp = powf(10.0f, 0.05f * decibels); + float adjAmp = (amp - minAmp) * inverseAmpRange; + amplitude = powf(adjAmp, 1.0f / root); + } + } + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:amplitude]; + + NSString* jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_POSITION, amplitude]; + [self.commandDelegate evalJs:jsString]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + @end @implementation CDVAudioFile http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/tests/tests.js ---------------------------------------------------------------------- diff --git a/tests/tests.js b/tests/tests.js index 246e40b..08c0ff7 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -25,7 +25,7 @@ // increased timeout for actual playback to give device chance to download and play mp3 file // some emulators can be REALLY slow at this, so two minutes var ACTUAL_PLAYBACK_TEST_TIMEOUT = 2 * 60 * 1000; - + var isWindows = cordova.platformId == 'windows8' || cordova.platformId == 'windows'; // detect whether audio hardware is available and enabled var isAudioSupported = isWindows ? Windows.Media.Devices.MediaDevice.getDefaultAudioRenderId(Windows.Media.Devices.AudioDeviceRole.default) : true; @@ -168,7 +168,14 @@ exports.defineAutoTests = function () { media1.release(); }); - it("media.spec.15 should return MediaError for bad filename", function (done) { + it("media.spec.15 should contain a getCurrentAmplitude function", function () { + var media1 = new Media("dummy"); + expect(media1.getCurrentAmplitude).toBeDefined(); + expect(typeof media1.getCurrentAmplitude).toBe('function'); + media1.release(); + }); + + it("media.spec.16 should return MediaError for bad filename", function (done) { //bb10 dialog pops up, preventing tests from running if (cordova.platformId === 'blackberry10') { pending(); @@ -203,7 +210,7 @@ exports.defineAutoTests = function () { } }); - it("media.spec.16 position should be set properly", function (done) { + it("media.spec.17 position should be set properly", function (done) { // no audio hardware available if (!isAudioSupported) { pending(); @@ -233,7 +240,7 @@ exports.defineAutoTests = function () { media.play(); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); - it("media.spec.17 duration should be set properly", function (done) { + it("media.spec.18 duration should be set properly", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } @@ -262,7 +269,7 @@ exports.defineAutoTests = function () { media.play(); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); - it("media.spec.20 should be able to resume playback after pause", function (done) { + it("media.spec.19 should be able to resume playback after pause", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } @@ -297,15 +304,15 @@ exports.defineAutoTests = function () { } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); - + // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking setTimeout(function() { media.play(); }, 4000); - + }, ACTUAL_PLAYBACK_TEST_TIMEOUT); - it("media.spec.21 should be able to seek through file", function (done) { + it("media.spec.20 should be able to seek through file", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } @@ -330,23 +337,23 @@ exports.defineAutoTests = function () { } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); - + // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking setTimeout(function() { media.play(); }, 4000); - + }, ACTUAL_PLAYBACK_TEST_TIMEOUT); }); - it("media.spec.18 should contain a setRate function", function () { + it("media.spec.21 should contain a setRate function", function () { var media1 = new Media("dummy"); expect(media1.setRate).toBeDefined(); expect(typeof media1.setRate).toBe('function'); media1.release(); }); - it("media.spec.19 playback rate should be set properly using setRate", function (done) { + it("media.spec.22 playback rate should be set properly using setRate", function (done) { if (cordova.platformId !== 'ios') { expect(true).toFailWithMessage('Platform does not supported this feature'); pending(); http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/3706b757/www/Media.js ---------------------------------------------------------------------- diff --git a/www/Media.js b/www/Media.js index d302cbe..192d4b3 100644 --- a/www/Media.js +++ b/www/Media.js @@ -163,6 +163,14 @@ Media.prototype.setRate = function(rate) { } }; +/** + * Get amplitude of audio. + */ +Media.prototype.getCurrentAmplitude = function(success, fail) { + exec(function(p) { + success(p); + }, fail, "Media", "getCurrentAmplitudeAudio", [this.id]); +}; /** * Audio has status update. @@ -232,4 +240,4 @@ if (cordova.platformId === 'android' || cordova.platformId === 'amazon-fireos' | exec(onMessageFromNative, undefined, 'Media', 'messageChannel', []); channel.initializationComplete('onMediaPluginReady'); }); -} \ No newline at end of file +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org