import type {EmitterSubscription} from 'react-native'; import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform, } from 'react-native'; const {RNAudioRecorderPlayer} = NativeModules; export enum AudioSourceAndroidType { DEFAULT = 0, MIC, VOICE_UPLINK, VOICE_DOWNLINK, VOICE_CALL, CAMCORDER, VOICE_RECOGNITION, VOICE_COMMUNICATION, REMOTE_SUBMIX, UNPROCESSED, RADIO_TUNER = 1998, HOTWORD, } export enum OutputFormatAndroidType { DEFAULT = 0, THREE_GPP, MPEG_4, AMR_NB, AMR_WB, AAC_ADIF, AAC_ADTS, OUTPUT_FORMAT_RTP_AVP, MPEG_2_TS, WEBM, UNUSED, OGG, } export enum AudioEncoderAndroidType { DEFAULT = 0, AMR_NB, AMR_WB, AAC, HE_AAC, AAC_ELD, VORBIS, OPUS, } export enum AVEncodingOption { lpcm = 'lpcm', ima4 = 'ima4', aac = 'aac', MAC3 = 'MAC3', MAC6 = 'MAC6', ulaw = 'ulaw', alaw = 'alaw', mp1 = 'mp1', mp2 = 'mp2', mp4 = 'mp4', alac = 'alac', amr = 'amr', flac = 'flac', opus = 'opus', wav = 'wav', } type AVEncodingType = | AVEncodingOption.lpcm | AVEncodingOption.ima4 | AVEncodingOption.aac | AVEncodingOption.MAC3 | AVEncodingOption.MAC6 | AVEncodingOption.ulaw | AVEncodingOption.alaw | AVEncodingOption.mp1 | AVEncodingOption.mp2 | AVEncodingOption.mp4 | AVEncodingOption.alac | AVEncodingOption.amr | AVEncodingOption.flac | AVEncodingOption.opus | AVEncodingOption.wav; export enum AVModeIOSOption { gamechat = 'gamechat', measurement = 'measurement', movieplayback = 'movieplayback', spokenaudio = 'spokenaudio', videochat = 'videochat', videorecording = 'videorecording', voicechat = 'voicechat', voiceprompt = 'voiceprompt', } export type AVModeIOSType = | AVModeIOSOption.gamechat | AVModeIOSOption.measurement | AVModeIOSOption.movieplayback | AVModeIOSOption.spokenaudio | AVModeIOSOption.videochat | AVModeIOSOption.videorecording | AVModeIOSOption.voicechat | AVModeIOSOption.voiceprompt; export enum AVEncoderAudioQualityIOSType { min = 0, low = 32, medium = 64, high = 96, max = 127, } export enum AVLinearPCMBitDepthKeyIOSType { 'bit8' = 8, 'bit16' = 16, 'bit24' = 24, 'bit32' = 32, } export interface AudioSet { AVSampleRateKeyIOS?: number; AVFormatIDKeyIOS?: AVEncodingType; AVModeIOS?: AVModeIOSType; AVNumberOfChannelsKeyIOS?: number; AVEncoderAudioQualityKeyIOS?: AVEncoderAudioQualityIOSType; AudioSourceAndroid?: AudioSourceAndroidType; AVLinearPCMBitDepthKeyIOS?: AVLinearPCMBitDepthKeyIOSType; AVLinearPCMIsBigEndianKeyIOS?: boolean; AVLinearPCMIsFloatKeyIOS?: boolean; AVLinearPCMIsNonInterleavedIOS?: boolean; AVEncoderBitRateKeyIOS?: number; OutputFormatAndroid?: OutputFormatAndroidType; AudioEncoderAndroid?: AudioEncoderAndroidType; AudioEncodingBitRateAndroid?: number; AudioSamplingRateAndroid?: number; AudioChannelsAndroid?: number; } const pad = (num: number): string => { return ('0' + num).slice(-2); }; export type RecordBackType = { isRecording?: boolean; currentPosition: number; currentMetering?: number; }; export type PlayBackType = { isMuted?: boolean; currentPosition: number; duration: number; isFinished: boolean; }; class AudioRecorderPlayer { private _isRecording: boolean; private _isPlaying: boolean; private _hasPaused: boolean; private _hasPausedRecord: boolean; private _recorderSubscription: EmitterSubscription; private _playerSubscription: EmitterSubscription; private _playerCallback: (event: PlayBackType) => void; mmss = (secs: number): string => { let minutes = Math.floor(secs / 60); secs = secs % 60; minutes = minutes % 60; return pad(minutes) + ':' + pad(secs); }; mmssss = (milisecs: number): string => { const secs = Math.floor(milisecs / 1000); const minutes = Math.floor(secs / 60); const seconds = secs % 60; const miliseconds = Math.floor((milisecs % 1000) / 10); return pad(minutes) + ':' + pad(seconds) + ':' + pad(miliseconds); }; /** * Set listerner from native module for recorder. * @returns {callBack((e: RecordBackType): void)} */ addRecordBackListener = ( callback: (recordingMeta: RecordBackType) => void, ): void => { if (Platform.OS === 'android') { this._recorderSubscription = DeviceEventEmitter.addListener( 'rn-recordback', callback, ); } else { const myModuleEvt = new NativeEventEmitter(RNAudioRecorderPlayer); this._recorderSubscription = myModuleEvt.addListener( 'rn-recordback', callback, ); } }; /** * Remove listener for recorder. * @returns {void} */ removeRecordBackListener = (): void => { if (this._recorderSubscription) { this._recorderSubscription.remove(); this._recorderSubscription = null; } }; /** * Set listener from native module for player. * @returns {callBack((e: PlayBackType): void)} */ addPlayBackListener = ( callback: (playbackMeta: PlayBackType) => void, ): void => { this._playerCallback = callback; }; /** * remove listener for player. * @returns {void} */ removePlayBackListener = (): void => { this._playerCallback = null; }; /** * start recording with param. * @param {string} uri audio uri. * @returns {Promise} */ startRecorder = async ( uri?: string, audioSets?: AudioSet, meteringEnabled?: boolean, ): Promise => { if (!this._isRecording) { this._isRecording = true; try { return await RNAudioRecorderPlayer.startRecorder( uri ?? 'DEFAULT', audioSets, meteringEnabled ?? false, ); } catch (error: any) { this._isRecording = false; throw error; } } return 'Already recording'; }; /** * Pause recording. * @returns {Promise} */ pauseRecorder = async (): Promise => { if (!this._hasPausedRecord) { this._hasPausedRecord = true; return RNAudioRecorderPlayer.pauseRecorder(); } return 'Already paused recording.'; }; /** * Resume recording. * @returns {Promise} */ resumeRecorder = async (): Promise => { if (this._hasPausedRecord) { this._hasPausedRecord = false; return RNAudioRecorderPlayer.resumeRecorder(); } return 'Currently recording.'; }; /** * stop recording. * @returns {Promise} */ stopRecorder = async (): Promise => { if (this._isRecording) { this._isRecording = false; this._hasPausedRecord = false; return RNAudioRecorderPlayer.stopRecorder(); } return 'Already stopped'; }; /** * Resume playing. * @returns {Promise} */ resumePlayer = async (): Promise => { if (!this._isPlaying) { return 'No audio playing'; } if (this._hasPaused) { this._hasPaused = false; return RNAudioRecorderPlayer.resumePlayer(); } return 'Already playing'; }; playerCallback = (event: PlayBackType): void => { if (this._playerCallback) { this._playerCallback(event); } if (event.isFinished) { this.stopPlayer(); } }; /** * Start playing with param. * @param {string} uri audio uri. * @param {Record} httpHeaders Set of http headers. * @returns {Promise} */ startPlayer = async ( uri?: string, httpHeaders?: Record, ): Promise => { if (!uri) { uri = 'DEFAULT'; } if (!this._playerSubscription) { if (Platform.OS === 'android') { this._playerSubscription = DeviceEventEmitter.addListener( 'rn-playback', this.playerCallback, ); } else { const myModuleEvt = new NativeEventEmitter(RNAudioRecorderPlayer); this._playerSubscription = myModuleEvt.addListener( 'rn-playback', this.playerCallback, ); } } if (!this._isPlaying || this._hasPaused) { this._isPlaying = true; this._hasPaused = false; return RNAudioRecorderPlayer.startPlayer(uri, httpHeaders); } }; /** * Stop playing. * @returns {Promise} */ stopPlayer = async (): Promise => { if (this._isPlaying) { this._isPlaying = false; this._hasPaused = false; return RNAudioRecorderPlayer.stopPlayer(); } return 'Already stopped playing'; }; /** * Pause playing. * @returns {Promise} */ pausePlayer = async (): Promise => { if (!this._isPlaying) { return 'No audio playing'; } if (!this._hasPaused) { this._hasPaused = true; return RNAudioRecorderPlayer.pausePlayer(); } }; /** * Seek to. * @param {number} time position seek to in millisecond. * @returns {Promise} */ seekToPlayer = async (time: number): Promise => { return RNAudioRecorderPlayer.seekToPlayer(time); }; /** * Set volume. * @param {number} setVolume set volume. * @returns {Promise} */ setVolume = async (volume: number): Promise => { if (volume < 0 || volume > 1) { throw new Error('Value of volume should be between 0.0 to 1.0'); } return RNAudioRecorderPlayer.setVolume(volume); }; /** * Set playback speed. * @param {number} setPlaybackSpeed set playback speed. * @returns {Promise} */ setPlaybackSpeed = async (playbackSpeed: number): Promise => { return RNAudioRecorderPlayer.setPlaybackSpeed(playbackSpeed); }; /** * Set subscription duration. Default is 0.5. * @param {number} sec subscription callback duration in seconds. * @returns {Promise} */ setSubscriptionDuration = async (sec: number): Promise => { return RNAudioRecorderPlayer.setSubscriptionDuration(sec); }; } export default AudioRecorderPlayer;