Skip to content

Commit c9a3bf1

Browse files
davecoffinDave Coffin
and
Dave Coffin
authored
feat: respect ios sampleRate and adds one for AVAudioQuality (#198)
Co-authored-by: Dave Coffin <[email protected]>
1 parent 241f790 commit c9a3bf1

File tree

5 files changed

+109
-33
lines changed

5 files changed

+109
-33
lines changed

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,45 @@ export class YourClass {
110110
// Android only: extra detail on error
111111
console.log('extra info on the error:', args.extra);
112112
}
113+
114+
// This is an example method for watching audio meters and converting the values from Android's arbitrary
115+
// value to something close to dB. iOS reports values from -120 to 0, android reports values from 0 to about 37000.
116+
// The below method converts the values to db as close as I could figure out. You can tweak the .1 value to your discretion.
117+
// I am basically converting these numbers to something close to a percentage value. My handle Meter UI method
118+
// converts that value to a value I can use to pulse a circle bigger and smaller, representing your audio level.
119+
private _initMeter() {
120+
this._resetMeter();
121+
this._meterInterval = this._win.setInterval(() => {
122+
this.audioMeter = this._recorder.getMeters();
123+
if (isIOS) {
124+
this.handleMeterUI(this.audioMeter+200)
125+
} else {
126+
let db = (20 * Math.log10(parseInt(this.audioMeter) / .1));
127+
let percentage = db + 85;
128+
this.handleMeterUI(percentage)
129+
}
130+
}, 150);
131+
}
132+
133+
handleMeterUI(percentage) {
134+
let scale = percentage/100;
135+
136+
function map_range(value, in_low, in_high, out_low, out_high) {
137+
return out_low + (out_high - out_low) * (value - in_low) / (in_high - in_low);
138+
}
139+
let lerpScale = map_range(scale, 1.2, 1.9, 0.1, 2.1)
140+
if (scale > 0) {
141+
this.levelMeterCircleUI.animate({
142+
scale: {x: lerpScale, y: lerpScale},
143+
duration: 100
144+
}).then(() => {}).catch(() => {})
145+
}
146+
if (lerpScale > 2.2) {
147+
this.levelBgColor = 'rgba(255, 0, 0, 1)';
148+
} else {
149+
this.levelBgColor = 'rgb(0, 183, 0)';
150+
}
151+
}
113152
}
114153
```
115154

@@ -170,6 +209,23 @@ player
170209
| android | Get the native MediaRecorder class instance. |
171210
| debug | Set true to enable debugging console logs (default false). |
172211

212+
#### TNSRecorder AudioRecorderOptions
213+
214+
| Property | Type | Description |
215+
| -------- | ------ |---------------------------------------------------------- |
216+
| filename | string | Gets or sets the recorded file name. |
217+
| source | int |**Android Only** Sets the source for recording. Learn more here https://developer.android.com/reference/android/media/MediaRecorder.AudioSource |
218+
| maxDuration | int |Gets or set the max duration of the recording session. Input in milliseconds, which is Android's format. Will be converted appropriately for iOS. |
219+
| metering | boolean |Enables metering. This will allow you to inspect the audio level by calling the record instance's `getMeters` ,method. This will return dB on iOS, but an arbitrary amplitude number for Android. See the metering example for a way to convert the output to something resembling dB on Android. |
220+
| format | int or enum |The Audio format to record in. On Android, use these Enums: https://developer.android.com/reference/android/media/AudioFormat#ENCODING_PCM_16BIT On ios, use these format options: https://developer.apple.com/documentation/coreaudiotypes/1572096-audio_format_identifiers |
221+
| channels | int | Number of channels to record (mono, st) |
222+
| sampleRate | int | The sample rate to record in. Default: 44100 |
223+
| bitRate | int | **Android Only** The bitrate to record in. iOS automatically calculates based on `iosAudioQuality` flag. Default: 128000 |
224+
| encoder | int or enum | **Android Only** Use https://developer.android.com/reference/android/media/MediaRecorder.AudioEncoder#AAC |
225+
| iosAudioQuality | string | ios uses AVAudioQuality to determine encoder and bitrate. Accepts Min, Low, Medium, High, Max https://developer.apple.com/documentation/avfaudio/avaudioquality |
226+
| errorCallback | function | Gets or sets the callback when an error occurs with the media recorder. Returns An object containing the native values for the error callback. |
227+
| infoCallback | function | Gets or sets the callback to be invoked to communicate some info and/or warning about the media or its playback. Returns An object containing the native values for the info callback. |
228+
173229
### Player
174230

175231
#### TNSPlayer Methods

src/android/recorder.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,26 @@ export class TNSRecorder implements TNSRecordI {
5959
} else {
6060
this._recorder = new android.media.MediaRecorder();
6161
}
62-
63-
const audioSource = options.source ? options.source : 0;
62+
63+
const audioSource = options.source ? options.source : android.media.MediaRecorder.AudioSource.DEFAULT; // https://developer.android.com/reference/android/media/MediaRecorder.AudioSource
6464
this._recorder.setAudioSource(audioSource);
6565

66-
const outFormat = options.format ? options.format : 0;
66+
const outFormat = options.format ? options.format : android.media.AudioFormat.ENCODING_PCM_16BIT; // https://developer.android.com/reference/android/media/AudioFormat#ENCODING_PCM_16BIT
6767
this._recorder.setOutputFormat(outFormat);
6868

69-
const encoder = options.encoder ? options.encoder : 0;
69+
const encoder = options.encoder ? options.encoder : android.media.MediaRecorder.AudioEncoder.AAC; // https://developer.android.com/reference/android/media/MediaRecorder.AudioEncoder#AAC
7070
this._recorder.setAudioEncoder(encoder);
7171

7272
if (options.channels) {
7373
this._recorder.setAudioChannels(options.channels);
7474
}
75-
if (options.sampleRate) {
76-
this._recorder.setAudioSamplingRate(options.sampleRate);
77-
}
78-
if (options.bitRate) {
79-
this._recorder.setAudioEncodingBitRate(options.bitRate);
80-
}
75+
76+
let sampleRate = options.sampleRate ? options.sampleRate : 44100;
77+
this._recorder.setAudioSamplingRate(sampleRate);
78+
79+
let bitRate = options.bitRate ? options.bitRate : 128000;
80+
this._recorder.setAudioEncodingBitRate(bitRate);
81+
8182
if (options.maxDuration) {
8283
this._recorder.setMaxDuration(options.maxDuration);
8384
}

src/ios/recorder.ts

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Observable } from '@nativescript/core';
22
import { TNSRecordI } from '../common';
33
import { AudioRecorderOptions } from '../options';
4-
4+
declare var kAudioFormatAppleLossless, kAudioFormatMPEG4AAC;
55
@NativeClass()
66
class TNSRecorderDelegate
77
extends NSObject
@@ -81,36 +81,46 @@ export class TNSRecorder extends Observable implements TNSRecordI {
8181
this._recordingSession.setActiveError(true, null);
8282
this._recordingSession.requestRecordPermission((allowed: boolean) => {
8383
if (allowed) {
84-
// var recordSetting = new NSMutableDictionary((<any>[NSNumber.numberWithInt(kAudioFormatMPEG4AAC), NSNumber.numberWithInt((<any>AVAudioQuality).Medium.rawValue), NSNumber.numberWithFloat(16000.0), NSNumber.numberWithInt(1)]),
85-
// (<any>["AVFormatIDKey", "AVEncoderAudioQualityKey", "AVSampleRateKey", "AVNumberOfChannelsKey"]));
86-
8784
const recordSetting = NSMutableDictionary.alloc().init();
88-
89-
if (options.format) {
90-
recordSetting.setValueForKey(
91-
NSNumber.numberWithInt(options.format),
92-
'AVFormatIDKey'
93-
);
94-
} else {
95-
recordSetting.setValueForKey(
96-
NSNumber.numberWithInt(kAudioFormatMPEG4AAC),
97-
'AVFormatIDKey'
98-
);
85+
let format = options.format ? options.format : kAudioFormatAppleLossless;
86+
console.log(`setting format: ${format}`);
87+
recordSetting.setValueForKey(
88+
NSNumber.numberWithInt(format),
89+
'AVFormatIDKey'
90+
);
91+
92+
let avAudioQualityValue = AVAudioQuality.Medium;
93+
if (options.iosAudioQuality) {
94+
if (options.iosAudioQuality == 'Min') {
95+
avAudioQualityValue = AVAudioQuality.Min;
96+
} else if (options.iosAudioQuality == 'Low') {
97+
avAudioQualityValue = AVAudioQuality.Low
98+
} else if (options.iosAudioQuality == 'Medium') {
99+
avAudioQualityValue = AVAudioQuality.Medium
100+
} else if (options.iosAudioQuality == 'High') {
101+
avAudioQualityValue = AVAudioQuality.High
102+
} else if (options.iosAudioQuality == 'Max') {
103+
avAudioQualityValue = AVAudioQuality.Max
104+
}
99105
}
100-
// recordSetting.setValueForKey(
101-
// NSNumber.numberWithInt((<any>AVAudioQuality).Medium.rawValue),
102-
// 'AVEncoderAudioQualityKey'
103-
// );
106+
console.log(`setting format: ${avAudioQualityValue}`); // https://developer.apple.com/documentation/avfaudio/avaudioquality;
104107
recordSetting.setValueForKey(
105-
NSNumber.numberWithInt(AVAudioQuality.Medium),
108+
NSNumber.numberWithInt(avAudioQualityValue),
106109
'AVEncoderAudioQualityKey'
107110
);
111+
112+
let sampleRate: number = 44100.0;
113+
if (options.sampleRate) sampleRate = parseFloat(parseInt(options.sampleRate).toFixed(1));
114+
console.log(`setting sampleRate: ${sampleRate}`);
108115
recordSetting.setValueForKey(
109-
NSNumber.numberWithFloat(16000.0),
116+
NSNumber.numberWithFloat(sampleRate),
110117
'AVSampleRateKey'
111118
);
119+
120+
let channels = options.channels ? options.channels : 1;
121+
console.log(`setting channels: ${channels}`);
112122
recordSetting.setValueForKey(
113-
NSNumber.numberWithInt(1),
123+
NSNumber.numberWithInt(channels),
114124
'AVNumberOfChannelsKey'
115125
);
116126

src/options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ export interface AudioRecorderOptions {
9292
*/
9393
encoder?: any;
9494

95+
/**
96+
* Sets the ios audio quality setting. Options are Min|Low|Medium|High|Max. Set to Medium by default.
97+
*/
98+
iosAudioQuality?: string;
99+
95100
/**
96101
* Gets or sets the callback when an error occurs with the media recorder.
97102
* @returns {Object} An object containing the native values for the error callback.

src/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nativescript-audio",
3-
"version": "6.2.6",
3+
"version": "6.3.0",
44
"description": "NativeScript plugin to record and play audio.",
55
"main": "audio",
66
"typings": "index.d.ts",
@@ -62,6 +62,10 @@
6262
"email": "[email protected]",
6363
"url": "https://github.com/NathanWalker"
6464
},
65+
{
66+
"name": "Dave Coffin",
67+
"url": "https://github.com/davecoffin"
68+
},
6569
{
6670
"name": "Jibon Lawrence Costa",
6771
"url": "https://github.com/jibon57"

0 commit comments

Comments
 (0)