Skip to content

Commit fdbf720

Browse files
committed
feat(android): support for wav recording
1 parent ebe0f30 commit fdbf720

File tree

7 files changed

+88
-56
lines changed

7 files changed

+88
-56
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ player
148148

149149
### Recorder
150150

151+
On Android if you want to record PCM you need to use `ANDROID_ENCODER_PCM_16 | ` exported by this plugin for the `encoder` option
152+
While using PCM `infoCallback`, `errorCallback` and `_getMeters` are not used on Android
153+
151154
#### TNSRecorder Methods
152155

153156
| Method | Description |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
implementation 'com.github.squti:Android-Wave-Recorder:1.7.0'
3+
}

packages/audio/platforms/android/native-api-usage.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"android.media:PlaybackParams*",
1515
"android.media:MediaRecorder*",
1616
"android.media.MediaRecorder:OnErrorListener*",
17-
"android.media.MediaRecorder:OnInfoListener*"
17+
"android.media.MediaRecorder:OnInfoListener*",
18+
"com.github.squti.androidwaverecorder:WaveRecorder*"
1819
]
1920
}

src/audio/android/player.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export class TNSPlayer extends Observable {
248248
if (options.infoCallback) {
249249
player.setOnInfoListener(
250250
new android.media.MediaPlayer.OnInfoListener({
251-
onInfo: (player: any, info: number, extra: number) => {
251+
onInfo: (player: android.media.MediaPlayer, info: number, extra: number) => {
252252
options.infoCallback({ player, info, extra });
253253
return true;
254254
}
@@ -322,7 +322,7 @@ export class TNSPlayer extends Observable {
322322
// https://developer.android.com/reference/android/app/Activity.html#setVolumeControlStream(int)
323323
activity.setVolumeControlStream(android.media.AudioManager.STREAM_MUSIC);
324324

325-
// register the receiver so when calls or another app takes main audio focus the player pauses
325+
// register the receiver so when calls or another app takes main focus the player pauses
326326
Application.android.registerBroadcastReceiver(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY, (context: android.content.Context, intent: android.content.Intent) => {
327327
this.pause();
328328
});
@@ -392,7 +392,7 @@ export class TNSPlayer extends Observable {
392392

393393
public async getAudioTrackDuration() {
394394
const duration = this._player ? this._player.getDuration() : 0;
395-
return duration.toString();
395+
return duration;
396396
}
397397

398398
/**

src/audio/android/recorder.ts

+65-48
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import { check, request } from '@nativescript-community/perms';
22
import { Application } from '@nativescript/core';
3-
import { AudioRecorderOptions } from '..';
3+
import { ANDROID_ENCODER_PCM, ANDROID_ENCODER_PCM_16, AudioRecorderOptions } from '..';
44

55
export class TNSRecorder {
66
private _recorder: any;
7+
private _wavrecorder: any;
78

8-
get android() {
9-
return this._recorder;
10-
}
119

1210
public static CAN_RECORD(): boolean {
1311
const pManager = Application.android.context.getPackageManager();
@@ -32,57 +30,68 @@ export class TNSRecorder {
3230
// bake the permission into this so the dev doesn't have to call it
3331
await this.requestRecordPermission();
3432

33+
const audioSource = options.source ? options.source : 0;
3534
if (this._recorder) {
3635
// reset for reuse
3736
this._recorder.reset();
3837
} else {
39-
this._recorder = new android.media.MediaRecorder();
38+
if (options.encoder === ANDROID_ENCODER_PCM || options.encoder === ANDROID_ENCODER_PCM_16) {
39+
console.log('WaveRecorder');
40+
//@ts-ignore
41+
this._wavrecorder = new com.github.squti.androidwaverecorder.WaveRecorder(options.filename);
42+
} else {
43+
this._recorder = new android.media.MediaRecorder();
44+
}
4045
}
41-
42-
const audioSource = options.source ? options.source : 0;
43-
this._recorder.setAudioSource(audioSource);
44-
45-
const outFormat = options.format ? options.format : 0;
46-
this._recorder.setOutputFormat(outFormat);
47-
48-
const encoder = options.encoder ? options.encoder : 0;
49-
this._recorder.setAudioEncoder(encoder);
50-
51-
if (options.channels) {
52-
this._recorder.setAudioChannels(options.channels);
53-
}
54-
if (options.sampleRate) {
55-
this._recorder.setAudioSamplingRate(options.sampleRate);
56-
}
57-
if (options.bitRate) {
58-
this._recorder.setAudioEncodingBitRate(options.bitRate);
59-
}
60-
if (options.maxDuration) {
61-
this._recorder.setMaxDuration(options.maxDuration);
46+
if (this._recorder) {
47+
this._recorder.setAudioSource(audioSource);
48+
const outFormat = options.format ? options.format : 0;
49+
this._recorder.setOutputFormat(outFormat);
50+
51+
const encoder = options.encoder ? options.encoder : 0;
52+
this._recorder.setAudioEncoder(encoder);
53+
54+
if (options.channels) {
55+
this._recorder.setAudioChannels(options.channels);
56+
}
57+
if (options.sampleRate) {
58+
this._recorder.setAudioSamplingRate(options.sampleRate);
59+
}
60+
if (options.bitRate) {
61+
this._recorder.setAudioEncodingBitRate(options.bitRate);
62+
}
63+
if (options.maxDuration) {
64+
this._recorder.setMaxDuration(options.maxDuration);
65+
}
66+
// On Error
67+
options.errorCallback &&
68+
this._recorder.setOnErrorListener(
69+
new android.media.MediaRecorder.OnErrorListener({
70+
onError: (recorder: any, error: number, extra: number) => {
71+
options.errorCallback({ recorder, error, extra });
72+
}
73+
})
74+
);
75+
76+
// On Info
77+
options.infoCallback &&
78+
this._recorder.setOnInfoListener(
79+
new android.media.MediaRecorder.OnInfoListener({
80+
onInfo: (recorder: any, info: number, extra: number) => {
81+
options.infoCallback({ recorder, info, extra });
82+
}
83+
})
84+
);
85+
this._recorder.setOutputFile(options.filename);
86+
this._recorder.prepare();
87+
this._recorder.start();
88+
} else if (this._wavrecorder) {
89+
this._wavrecorder.waveConfig.sampleRate = options.sampleRate || 44100;
90+
this._wavrecorder.waveConfig.channels = options.channels === 1 ? android.media.AudioFormat.CHANNEL_IN_MONO : 2;
91+
this._wavrecorder.waveConfig.audioEncoding = options.encoder === ANDROID_ENCODER_PCM_16 ? android.media.AudioFormat.ENCODING_PCM_16BIT : android.media.AudioFormat.ENCODING_PCM_8BIT;
92+
this._wavrecorder.startRecording();
6293
}
6394

64-
this._recorder.setOutputFile(options.filename);
65-
66-
// On Error
67-
this._recorder.setOnErrorListener(
68-
new android.media.MediaRecorder.OnErrorListener({
69-
onError: (recorder: any, error: number, extra: number) => {
70-
options.errorCallback({ recorder, error, extra });
71-
}
72-
})
73-
);
74-
75-
// On Info
76-
this._recorder.setOnInfoListener(
77-
new android.media.MediaRecorder.OnInfoListener({
78-
onInfo: (recorder: any, info: number, extra: number) => {
79-
options.infoCallback({ recorder, info, extra });
80-
}
81-
})
82-
);
83-
84-
this._recorder.prepare();
85-
this._recorder.start();
8695
}
8796

8897
public getMeters(): number {
@@ -92,19 +101,27 @@ export class TNSRecorder {
92101

93102
public async pause() {
94103
if (this._recorder) {
104+
// not working yet with pcm
95105
this._recorder.pause();
106+
} else if (this._wavrecorder) {
107+
this._wavrecorder.pauseRecording();
96108
}
97109
}
98110

99111
public async resume() {
100112
if (this._recorder) {
113+
// not working yet with pcm
101114
this._recorder.resume();
115+
} else if (this._wavrecorder) {
116+
this._wavrecorder.resumeRecording();
102117
}
103118
}
104119

105120
public async stop() {
106121
if (this._recorder) {
107122
this._recorder.stop();
123+
} else if (this._wavrecorder) {
124+
this._wavrecorder.stopRecording();
108125
}
109126
}
110127

src/audio/common.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Utils, knownFolders, path as nsFilePath } from '@nativescript/core';
22

3+
4+
export const ANDROID_ENCODER_PCM = 161234;
5+
export const ANDROID_ENCODER_PCM_16 = 1612341;
6+
7+
38
export enum AudioPlayerEvents {
49
seek = 'seek',
510
paused = 'paused',

src/audio/index.d.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Observable } from '@nativescript/core';
22

3-
export { AudioPlayerEvents } from './common';
3+
export { AudioPlayerEvents, ANDROID_ENCODER_PCM, ANDROID_ENCODER_PCM_16 } from './common';
44
export interface AudioPlayerOptions {
55
/**
66
* The audio file to play.
@@ -21,7 +21,6 @@ export interface AudioPlayerOptions {
2121
* Set true to enable audio metering.
2222
*/
2323
metering?: boolean;
24-
audioMixing?: boolean;
2524

2625
pitch?: number;
2726

@@ -43,6 +42,11 @@ export interface AudioPlayerOptions {
4342
*/
4443
infoCallback?: Function;
4544

45+
/**
46+
* Should mix audio.
47+
*/
48+
audioMixing?: boolean;
49+
4650
/**
4751
* iOS: The category for playing recorded music or other sounds that are central to the successful use of your app.
4852
* https://developer.apple.com/documentation/avfaudio/avaudiosessioncategory?language=objc
@@ -191,7 +195,7 @@ export declare class TNSPlayer {
191195
/**
192196
* Get the duration of the audio file playing.
193197
*/
194-
getAudioTrackDuration(): Promise<string>;
198+
getAudioTrackDuration(): Promise<number>;
195199

196200
/**
197201
* Android Only
@@ -272,7 +276,6 @@ export declare class TNSRecorder {
272276
audioRecorderDidFinishRecording(recorder: any, success: boolean): void;
273277
}
274278

275-
276279
export enum AudioFocusDurationHint {
277280
/**
278281
* Expresses the fact that your application is now the sole source

0 commit comments

Comments
 (0)