diff --git a/examples/ExpoMessaging/yarn.lock b/examples/ExpoMessaging/yarn.lock index 1d9b849ec..9c73512b7 100644 --- a/examples/ExpoMessaging/yarn.lock +++ b/examples/ExpoMessaging/yarn.lock @@ -1881,14 +1881,6 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@gorhom/bottom-sheet@^4.6.4": - version "4.6.4" - resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-4.6.4.tgz#387d0f0f21e3470eb8575498cb81ce96f5108e79" - integrity sha512-0itLMblLBvepE065w3a60S030c2rNUsGshPC7wbWDm31VyqoaU2xjzh/ojH62YIJOcobBr5QoC30IxBBKDGovQ== - dependencies: - "@gorhom/portal" "1.0.14" - invariant "^2.2.4" - "@gorhom/bottom-sheet@^5.0.6": version "5.0.6" resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.0.6.tgz#f20736502399c7bcf8c73ea09e6b571dc07fe0eb" @@ -7034,24 +7026,6 @@ stream-buffers@2.2.x, stream-buffers@~2.2.0: version "0.0.0" uid "" -stream-chat-react-native-core@5.44.1: - version "5.44.1" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.44.1.tgz#0c4629ba01390574dabb963e2e805eb38602d2d4" - integrity sha512-cJqfsogmiTwK93kpPQhwaOl+whkH05JFl4k501r0VJbm6xlzv5eVb3NZfi6bX1z2xrILYHC5BFabM0RsX/xL/Q== - dependencies: - "@gorhom/bottom-sheet" "^4.6.4" - dayjs "1.10.5" - emoji-regex "^10.3.0" - i18next "^21.6.14" - intl-pluralrules "^2.0.1" - linkifyjs "^4.1.1" - lodash-es "4.17.21" - mime-types "^2.1.34" - path "0.12.7" - react-native-markdown-package "1.8.2" - react-native-url-polyfill "^1.3.0" - stream-chat "8.46.0" - "stream-chat-react-native-core@link:../../package": version "0.0.0" uid "" diff --git a/package/expo-package/src/index.js b/package/expo-package/src/index.js index 94363052f..f9fbff91d 100644 --- a/package/expo-package/src/index.js +++ b/package/expo-package/src/index.js @@ -11,6 +11,7 @@ import { getPhotos, iOS14RefreshGallerySelection, oniOS14GalleryLibrarySelectionChange, + overrideAudioRecordingConfiguration, pickDocument, pickImage, saveFile, @@ -31,6 +32,7 @@ registerNativeHandlers({ getPhotos, iOS14RefreshGallerySelection, oniOS14GalleryLibrarySelectionChange, + overrideAudioRecordingConfiguration, pickDocument, pickImage, saveFile, diff --git a/package/expo-package/src/optionalDependencies/Audio.ts b/package/expo-package/src/optionalDependencies/Audio.ts index c13c016c6..685696995 100644 --- a/package/expo-package/src/optionalDependencies/Audio.ts +++ b/package/expo-package/src/optionalDependencies/Audio.ts @@ -1,216 +1,13 @@ -import { AudioComponent, RecordingObject } from './AudioVideo'; - -export enum AndroidOutputFormat { - DEFAULT = 0, - THREE_GPP = 1, - MPEG_4 = 2, - AMR_NB = 3, - AMR_WB = 4, - AAC_ADIF = 5, - AAC_ADTS = 6, - RTP_AVP = 7, - MPEG2TS = 8, - WEBM = 9, -} - -// @docsMissing -export enum AndroidAudioEncoder { - DEFAULT = 0, - AMR_NB = 1, - AMR_WB = 2, - AAC = 3, - HE_AAC = 4, - AAC_ELD = 5, -} - -export enum IOSOutputFormat { - LINEARPCM = 'lpcm', - AC3 = 'ac-3', - '60958AC3' = 'cac3', - APPLEIMA4 = 'ima4', - MPEG4AAC = 'aac ', - MPEG4CELP = 'celp', - MPEG4HVXC = 'hvxc', - MPEG4TWINVQ = 'twvq', - MACE3 = 'MAC3', - MACE6 = 'MAC6', - ULAW = 'ulaw', - ALAW = 'alaw', - QDESIGN = 'QDMC', - QDESIGN2 = 'QDM2', - QUALCOMM = 'Qclp', - MPEGLAYER1 = '.mp1', - MPEGLAYER2 = '.mp2', - MPEGLAYER3 = '.mp3', - APPLELOSSLESS = 'alac', - MPEG4AAC_HE = 'aach', - MPEG4AAC_LD = 'aacl', - MPEG4AAC_ELD = 'aace', - MPEG4AAC_ELD_SBR = 'aacf', - MPEG4AAC_ELD_V2 = 'aacg', - MPEG4AAC_HE_V2 = 'aacp', - MPEG4AAC_SPATIAL = 'aacs', - AMR = 'samr', - AMR_WB = 'sawb', - AUDIBLE = 'AUDB', - ILBC = 'ilbc', - DVIINTELIMA = 0x6d730011, - MICROSOFTGSM = 0x6d730031, - AES3 = 'aes3', - ENHANCEDAC3 = 'ec-3', -} - -export enum IOSAudioQuality { - MIN = 0, - LOW = 0x20, - MEDIUM = 0x40, - HIGH = 0x60, - MAX = 0x7f, -} +import { + AndroidAudioEncoder, + AndroidOutputFormat, + ExpoAudioRecordingConfiguration as AudioRecordingConfiguration, + IOSAudioQuality, + IOSOutputFormat, + ExpoRecordingOptions as RecordingOptions, +} from 'stream-chat-react-native-core'; -export type RecordingOptionsAndroid = { - /** - * The desired audio encoder. See the [`AndroidAudioEncoder`](#androidaudioencoder) enum for all valid values. - */ - audioEncoder: AndroidAudioEncoder | number; - /** - * The desired file extension. Example valid values are `.3gp` and `.m4a`. - * For more information, see the [Android docs](https://developer.android.com/guide/topics/media/media-formats) - * for supported output formats. - */ - extension: string; - /** - * The desired file format. See the [`AndroidOutputFormat`](#androidoutputformat) enum for all valid values. - */ - outputFormat: AndroidOutputFormat | number; - /** - * The desired bit rate. - * - * Note that `prepareToRecordAsync()` may perform additional checks on the parameter to make sure whether the specified - * bit rate is applicable, and sometimes the passed bitRate will be clipped internally to ensure the audio recording - * can proceed smoothly based on the capabilities of the platform. - * - * @example `128000` - */ - bitRate?: number; - /** - * The desired maximum file size in bytes, after which the recording will stop (but `stopAndUnloadAsync()` must still - * be called after this point). - * - * @example `65536` - */ - maxFileSize?: number; - /** - * The desired number of channels. - * - * Note that `prepareToRecordAsync()` may perform additional checks on the parameter to make sure whether the specified - * number of audio channels are applicable. - * - * @example `1`, `2` - */ - numberOfChannels?: number; - /** - * The desired sample rate. - * - * Note that the sampling rate depends on the format for the audio recording, as well as the capabilities of the platform. - * For instance, the sampling rate supported by AAC audio coding standard ranges from 8 to 96 kHz, - * the sampling rate supported by AMRNB is 8kHz, and the sampling rate supported by AMRWB is 16kHz. - * Please consult with the related audio coding standard for the supported audio sampling rate. - * - * @example 44100 - */ - sampleRate?: number; -}; - -export type RecordingOptionsIOS = { - /** - * The desired audio quality. See the [`IOSAudioQuality`](#iosaudioquality) enum for all valid values. - */ - audioQuality: IOSAudioQuality | number; - /** - * The desired bit rate. - * - * @example `128000` - */ - bitRate: number; - /** - * The desired file extension. - * - * @example `'.caf'` - */ - extension: string; - /** - * The desired number of channels. - * - * @example `1`, `2` - */ - numberOfChannels: number; - /** - * The desired sample rate. - * - * @example `44100` - */ - sampleRate: number; - /** - * The desired bit depth hint. - * - * @example `16` - */ - bitDepthHint?: number; - /** - * The desired bit rate strategy. See the next section for an enumeration of all valid values of `bitRateStrategy`. - */ - bitRateStrategy?: number; - /** - * The desired PCM bit depth. - * - * @example `16` - */ - linearPCMBitDepth?: number; - /** - * A boolean describing if the PCM data should be formatted in big endian. - */ - linearPCMIsBigEndian?: boolean; - /** - * A boolean describing if the PCM data should be encoded in floating point or integral values. - */ - linearPCMIsFloat?: boolean; - /** - * The desired file format. See the [`IOSOutputFormat`](#iosoutputformat) enum for all valid values. - */ - outputFormat?: string | IOSOutputFormat | number; -}; - -// @docsMissing -export type RecordingOptionsWeb = { - bitsPerSecond?: number; - mimeType?: string; -}; - -export type RecordingOptions = { - /** - * Recording options for the Android platform. - */ - android: RecordingOptionsAndroid; - /** - * Recording options for the iOS platform. - */ - ios: RecordingOptionsIOS; - /** - * Recording options for the Web platform. - */ - web: RecordingOptionsWeb; - /** - * A boolean that determines whether audio level information will be part of the status object under the "metering" key. - */ - isMeteringEnabled?: boolean; - /** - * A boolean that hints to keep the audio active after `prepareToRecordAsync` completes. - * Setting this value can improve the speed at which the recording starts. Only set this value to `true` when you call `startAsync` - * immediately after `prepareToRecordAsync`. This value is automatically set when using `Audio.recording.createAsync()`. - */ - keepAudioActiveHint?: boolean; -}; +import { AudioComponent, RecordingObject } from './AudioVideo'; const sleep = (ms: number) => new Promise((resolve) => { @@ -221,6 +18,29 @@ const sleep = (ms: number) => class _Audio { recording: typeof RecordingObject | null = null; + audioRecordingConfiguration: AudioRecordingConfiguration = { + mode: { + allowsRecordingIOS: true, + playsInSilentModeIOS: true, + }, + options: { + android: { + audioEncoder: AndroidAudioEncoder.AAC, + extension: '.aac', + outputFormat: AndroidOutputFormat.AAC_ADTS, + }, + ios: { + audioQuality: IOSAudioQuality.HIGH, + bitRate: 128000, + extension: '.aac', + numberOfChannels: 2, + outputFormat: IOSOutputFormat.MPEG4AAC, + sampleRate: 44100, + }, + isMeteringEnabled: true, + web: {}, + }, + }; startRecording = async (recordingOptions: RecordingOptions, onRecordingStatusUpdate) => { try { @@ -242,28 +62,10 @@ class _Audio { if (!permissionsGranted) { throw new Error('Missing audio recording permission.'); } - await AudioComponent.setAudioModeAsync({ - allowsRecordingIOS: true, - playsInSilentModeIOS: true, - }); - const androidOptions = { - audioEncoder: AndroidAudioEncoder.AAC, - extension: '.aac', - outputFormat: AndroidOutputFormat.AAC_ADTS, - }; - const iosOptions = { - audioQuality: IOSAudioQuality.HIGH, - bitRate: 128000, - extension: '.aac', - numberOfChannels: 2, - outputFormat: IOSOutputFormat.MPEG4AAC, - sampleRate: 44100, - }; + await AudioComponent.setAudioModeAsync(this.audioRecordingConfiguration.mode); const options = { ...recordingOptions, - android: androidOptions, - ios: iosOptions, - web: {}, + ...this.audioRecordingConfiguration.options, }; // This is a band-aid fix for this (still unresolved) issue on Expo's side: @@ -303,4 +105,8 @@ class _Audio { }; } +export const overrideAudioRecordingConfiguration = ( + audioRecordingConfiguration: AudioRecordingConfiguration, +) => audioRecordingConfiguration; + export const Audio = AudioComponent ? new _Audio() : null; diff --git a/package/native-package/src/index.js b/package/native-package/src/index.js index ce4f4b104..c782abf54 100644 --- a/package/native-package/src/index.js +++ b/package/native-package/src/index.js @@ -12,6 +12,7 @@ import { getPhotos, iOS14RefreshGallerySelection, oniOS14GalleryLibrarySelectionChange, + overrideAudioRecordingConfiguration, pickDocument, pickImage, saveFile, @@ -32,6 +33,7 @@ registerNativeHandlers({ getPhotos, iOS14RefreshGallerySelection, oniOS14GalleryLibrarySelectionChange, + overrideAudioRecordingConfiguration, pickDocument, pickImage, saveFile, diff --git a/package/native-package/src/optionalDependencies/Audio.ts b/package/native-package/src/optionalDependencies/Audio.ts index 6795a0e27..e3bb05866 100644 --- a/package/native-package/src/optionalDependencies/Audio.ts +++ b/package/native-package/src/optionalDependencies/Audio.ts @@ -1,4 +1,16 @@ import { PermissionsAndroid, Platform } from 'react-native'; + +import { + AudioEncoderAndroidType, + RNCLIAudioRecordingConfiguration as AudioRecordingConfiguration, + AudioSourceAndroidType, + AVEncoderAudioQualityIOSType, + AVEncodingOption, + AVModeIOSOption, + OutputFormatAndroidType, + RNCLIRecordingOptions as RecordingOptions, +} from 'stream-chat-react-native-core'; + let AudioRecorderPackage; let audioRecorderPlayer; @@ -18,105 +30,6 @@ try { console.log('react-native-audio-recorder-player is not installed.'); } -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, -} - -export enum AudioEncoderAndroidType { - DEFAULT = 0, - AMR_NB, - AMR_WB, - AAC, - HE_AAC, - AAC_ELD, - VORBIS, -} - -export enum AVEncodingOption { - aac = 'aac', - alac = 'alac', - alaw = 'alaw', - amr = 'amr', - flac = 'flac', - ima4 = 'ima4', - lpcm = 'lpcm', - MAC3 = 'MAC3', - MAC6 = 'MAC6', - mp1 = 'mp1', - mp2 = 'mp2', - mp4 = 'mp4', - opus = 'opus', - ulaw = 'ulaw', - wav = '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 type RecordingOptions = { - /** - * A boolean that determines whether audio level information will be part of the status object under the "metering" key. - */ - isMeteringEnabled?: boolean; -}; - const verifyAndroidPermissions = async () => { const isRN71orAbove = Platform.constants.reactNativeVersion?.minor >= 71; const isAndroid13orAbove = (Platform.Version as number) >= 33; @@ -175,6 +88,20 @@ const verifyAndroidPermissions = async () => { }; class _Audio { + audioRecordingConfiguration: AudioRecordingConfiguration = { + options: { + audioSet: { + AudioEncoderAndroid: AudioEncoderAndroidType.AAC, + AudioSourceAndroid: AudioSourceAndroidType.MIC, + AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high, + AVFormatIDKeyIOS: AVEncodingOption.aac, + AVModeIOS: AVModeIOSOption.spokenaudio, + AVNumberOfChannelsKeyIOS: 2, + OutputFormatAndroid: OutputFormatAndroidType.AAC_ADTS, + }, + isMeteringEnabled: true, + }, + }; pausePlayer = async () => { await audioRecorderPlayer.pausePlayer(); }; @@ -206,19 +133,11 @@ class _Audio { android: `${RNBlobUtil.fs.dirs.CacheDir}/sound.aac`, ios: 'sound.aac', }); - const audioSet = { - AudioEncoderAndroid: AudioEncoderAndroidType.AAC, - AudioSourceAndroid: AudioSourceAndroidType.MIC, - AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high, - AVFormatIDKeyIOS: AVEncodingOption.aac, - AVModeIOS: AVModeIOSOption.measurement, - AVNumberOfChannelsKeyIOS: 2, - OutputFormatAndroid: OutputFormatAndroidType.AAC_ADTS, - }; + console.log('ISE: AUDIO: ', this.audioRecordingConfiguration); const recording = await audioRecorderPlayer.startRecorder( path, - audioSet, - options?.isMeteringEnabled, + this.audioRecordingConfiguration.options.audioSet, + this.audioRecordingConfiguration.options.isMeteringEnabled, ); audioRecorderPlayer.addRecordBackListener((status) => { @@ -255,4 +174,8 @@ class _Audio { }; } +export const overrideAudioRecordingConfiguration = ( + audioRecordingConfiguration: AudioRecordingConfiguration, +) => audioRecordingConfiguration; + export const Audio = AudioRecorderPackage && RNBlobUtil ? new _Audio() : null; diff --git a/package/src/native.ts b/package/src/native.ts index b097de15f..59a053a09 100644 --- a/package/src/native.ts +++ b/package/src/native.ts @@ -212,7 +212,12 @@ export type RecordingOptions = { isMeteringEnabled?: boolean; }; +export type AudioRecordingConfiguration = { + options?: RecordingOptions; +}; + export type AudioType = { + audioRecordingConfiguration: AudioRecordingConfiguration; startRecording: ( options?: RecordingOptions, onRecordingStatusUpdate?: (recordingStatus: RecordingStatus) => void, @@ -284,6 +289,9 @@ type Handlers = { getPhotos?: GetPhotos; iOS14RefreshGallerySelection?: iOS14RefreshGallerySelection; oniOS14GalleryLibrarySelectionChange?: OniOS14LibrarySelectionChange; + overrideAudioRecordingConfiguration?: ( + audioRecordingConfiguration: AudioRecordingConfiguration, + ) => AudioRecordingConfiguration; pickDocument?: PickDocument; pickImage?: PickImage; saveFile?: SaveFile; @@ -297,10 +305,16 @@ type Handlers = { }; export const registerNativeHandlers = (handlers: Handlers) => { - if (handlers.Audio !== null) { + if (handlers.Audio !== undefined) { Audio = handlers.Audio; } + if (Audio && handlers.overrideAudioRecordingConfiguration) { + Audio.audioRecordingConfiguration = handlers.overrideAudioRecordingConfiguration( + Audio.audioRecordingConfiguration, + ); + } + if (handlers.compressImage) { compressImage = handlers.compressImage; } @@ -345,7 +359,7 @@ export const registerNativeHandlers = (handlers: Handlers) => { SDK = handlers.SDK; } - if (handlers.shareImage !== null) { + if (handlers.shareImage !== undefined) { shareImage = handlers.shareImage; } diff --git a/package/src/types/types.ts b/package/src/types/types.ts index af9cae5c8..53443dabd 100644 --- a/package/src/types/types.ts +++ b/package/src/types/types.ts @@ -100,3 +100,432 @@ export interface DefaultStreamChatGenerics extends ExtendableGenerics { export type UnknownType = Record; export type ValueOf = T[keyof T]; + +// ASYNC AUDIO EXPO: + +export enum AndroidOutputFormat { + DEFAULT = 0, + THREE_GPP = 1, + MPEG_4 = 2, + AMR_NB = 3, + AMR_WB = 4, + AAC_ADIF = 5, + AAC_ADTS = 6, + RTP_AVP = 7, + MPEG2TS = 8, + WEBM = 9, +} + +// @docsMissing +export enum AndroidAudioEncoder { + DEFAULT = 0, + AMR_NB = 1, + AMR_WB = 2, + AAC = 3, + HE_AAC = 4, + AAC_ELD = 5, +} + +export enum IOSOutputFormat { + LINEARPCM = 'lpcm', + AC3 = 'ac-3', + '60958AC3' = 'cac3', + APPLEIMA4 = 'ima4', + MPEG4AAC = 'aac ', + MPEG4CELP = 'celp', + MPEG4HVXC = 'hvxc', + MPEG4TWINVQ = 'twvq', + MACE3 = 'MAC3', + MACE6 = 'MAC6', + ULAW = 'ulaw', + ALAW = 'alaw', + QDESIGN = 'QDMC', + QDESIGN2 = 'QDM2', + QUALCOMM = 'Qclp', + MPEGLAYER1 = '.mp1', + MPEGLAYER2 = '.mp2', + MPEGLAYER3 = '.mp3', + APPLELOSSLESS = 'alac', + MPEG4AAC_HE = 'aach', + MPEG4AAC_LD = 'aacl', + MPEG4AAC_ELD = 'aace', + MPEG4AAC_ELD_SBR = 'aacf', + MPEG4AAC_ELD_V2 = 'aacg', + MPEG4AAC_HE_V2 = 'aacp', + MPEG4AAC_SPATIAL = 'aacs', + AMR = 'samr', + AMR_WB = 'sawb', + AUDIBLE = 'AUDB', + ILBC = 'ilbc', + DVIINTELIMA = 0x6d730011, + MICROSOFTGSM = 0x6d730031, + AES3 = 'aes3', + ENHANCEDAC3 = 'ec-3', +} + +export enum IOSAudioQuality { + MIN = 0, + LOW = 0x20, + MEDIUM = 0x40, + HIGH = 0x60, + MAX = 0x7f, +} + +export type RecordingOptionsAndroid = { + /** + * The desired audio encoder. See the [`AndroidAudioEncoder`](#androidaudioencoder) enum for all valid values. + */ + audioEncoder: AndroidAudioEncoder | number; + /** + * The desired file extension. Example valid values are `.3gp` and `.m4a`. + * For more information, see the [Android docs](https://developer.android.com/guide/topics/media/media-formats) + * for supported output formats. + */ + extension: string; + /** + * The desired file format. See the [`AndroidOutputFormat`](#androidoutputformat) enum for all valid values. + */ + outputFormat: AndroidOutputFormat | number; + /** + * The desired bit rate. + * + * Note that `prepareToRecordAsync()` may perform additional checks on the parameter to make sure whether the specified + * bit rate is applicable, and sometimes the passed bitRate will be clipped internally to ensure the audio recording + * can proceed smoothly based on the capabilities of the platform. + * + * @example `128000` + */ + bitRate?: number; + /** + * The desired maximum file size in bytes, after which the recording will stop (but `stopAndUnloadAsync()` must still + * be called after this point). + * + * @example `65536` + */ + maxFileSize?: number; + /** + * The desired number of channels. + * + * Note that `prepareToRecordAsync()` may perform additional checks on the parameter to make sure whether the specified + * number of audio channels are applicable. + * + * @example `1`, `2` + */ + numberOfChannels?: number; + /** + * The desired sample rate. + * + * Note that the sampling rate depends on the format for the audio recording, as well as the capabilities of the platform. + * For instance, the sampling rate supported by AAC audio coding standard ranges from 8 to 96 kHz, + * the sampling rate supported by AMRNB is 8kHz, and the sampling rate supported by AMRWB is 16kHz. + * Please consult with the related audio coding standard for the supported audio sampling rate. + * + * @example 44100 + */ + sampleRate?: number; +}; + +export type RecordingOptionsIOS = { + /** + * The desired audio quality. See the [`IOSAudioQuality`](#iosaudioquality) enum for all valid values. + */ + audioQuality: IOSAudioQuality | number; + /** + * The desired bit rate. + * + * @example `128000` + */ + bitRate: number; + /** + * The desired file extension. + * + * @example `'.caf'` + */ + extension: string; + /** + * The desired number of channels. + * + * @example `1`, `2` + */ + numberOfChannels: number; + /** + * The desired sample rate. + * + * @example `44100` + */ + sampleRate: number; + /** + * The desired bit depth hint. + * + * @example `16` + */ + bitDepthHint?: number; + /** + * The desired bit rate strategy. See the next section for an enumeration of all valid values of `bitRateStrategy`. + */ + bitRateStrategy?: number; + /** + * The desired PCM bit depth. + * + * @example `16` + */ + linearPCMBitDepth?: number; + /** + * A boolean describing if the PCM data should be formatted in big endian. + */ + linearPCMIsBigEndian?: boolean; + /** + * A boolean describing if the PCM data should be encoded in floating point or integral values. + */ + linearPCMIsFloat?: boolean; + /** + * The desired file format. See the [`IOSOutputFormat`](#iosoutputformat) enum for all valid values. + */ + outputFormat?: string | IOSOutputFormat | number; +}; + +// @docsMissing +export type RecordingOptionsWeb = { + bitsPerSecond?: number; + mimeType?: string; +}; + +export type ExpoRecordingOptions = { + /** + * Recording options for the Android platform. + */ + android: RecordingOptionsAndroid; + /** + * Recording options for the iOS platform. + */ + ios: RecordingOptionsIOS; + /** + * Recording options for the Web platform. + */ + web: RecordingOptionsWeb; + /** + * A boolean that determines whether audio level information will be part of the status object under the "metering" key. + */ + isMeteringEnabled?: boolean; + /** + * A boolean that hints to keep the audio active after `prepareToRecordAsync` completes. + * Setting this value can improve the speed at which the recording starts. Only set this value to `true` when you call `startAsync` + * immediately after `prepareToRecordAsync`. This value is automatically set when using `Audio.recording.createAsync()`. + */ + keepAudioActiveHint?: boolean; +}; + +export type AudioMode = { + /** + * A boolean selecting if recording is enabled on iOS. + * > When this flag is set to `true`, playback may be routed to the phone earpiece instead of to the speaker. Set it back to `false` after stopping recording to reenable playback through the speaker. + * @default false + */ + allowsRecordingIOS: boolean; + /** + * An enum selecting how your experience's audio should interact with the audio from other apps on Android. + */ + interruptionModeAndroid: InterruptionModeAndroid; + /** + * An enum selecting how your experience's audio should interact with the audio from other apps on iOS. + */ + interruptionModeIOS: InterruptionModeIOS; + /** + * A boolean selecting if your experience's audio should play in silent mode on iOS. + * @default false + */ + playsInSilentModeIOS: boolean; + /** + * A boolean selecting if the audio is routed to earpiece on Android. + * @default false + */ + playThroughEarpieceAndroid: boolean; + /** + * A boolean selecting if your experience's audio should automatically be lowered in volume ("duck") if audio from another + * app interrupts your experience. If `false`, audio from other apps will pause your audio. + * @default true + */ + shouldDuckAndroid: boolean; + /** + * A boolean selecting if the audio session (playback or recording) should stay active even when the app goes into background. + * > This is not available in Expo Go for iOS, it will only work in standalone apps. + * > To enable it for standalone apps, [follow the instructions below](#playing-or-recording-audio-in-background) + * > to add `UIBackgroundModes` to your app configuration. + * @default false + */ + staysActiveInBackground: boolean; +}; + +// @needsAudit +export enum InterruptionModeIOS { + /** + * **This is the default option.** If this option is set, your experience's audio is mixed with audio playing in background apps. + */ + MixWithOthers = 0, + /** + * If this option is set, your experience's audio interrupts audio from other apps. + */ + DoNotMix = 1, + /** + * If this option is set, your experience's audio lowers the volume ("ducks") of audio from other apps while your audio plays. + */ + DuckOthers = 2, +} + +export enum InterruptionModeAndroid { + /** + * If this option is set, your experience's audio interrupts audio from other apps. + */ + DoNotMix = 1, + /** + * **This is the default option.** If this option is set, your experience's audio lowers the volume ("ducks") of audio from other apps while your audio plays. + */ + DuckOthers = 2, +} + +export type ExpoAudioRecordingConfiguration = { + mode?: Partial; + options?: Partial; +}; + +// ASYNC AUDIO RN CLI + +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, +} + +export enum AudioEncoderAndroidType { + DEFAULT = 0, + AMR_NB, + AMR_WB, + AAC, + HE_AAC, + AAC_ELD, + VORBIS, +} + +export enum AVEncodingOption { + aac = 'aac', + alac = 'alac', + alaw = 'alaw', + amr = 'amr', + flac = 'flac', + ima4 = 'ima4', + lpcm = 'lpcm', + MAC3 = 'MAC3', + MAC6 = 'MAC6', + mp1 = 'mp1', + mp2 = 'mp2', + mp4 = 'mp4', + opus = 'opus', + ulaw = 'ulaw', + wav = '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, +} + +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 interface AudioSet { + AudioChannelsAndroid?: number; + AudioEncoderAndroid?: AudioEncoderAndroidType; + AudioEncodingBitRateAndroid?: number; + AudioSamplingRateAndroid?: number; + AudioSourceAndroid?: AudioSourceAndroidType; + AVEncoderAudioQualityKeyIOS?: AVEncoderAudioQualityIOSType; + AVEncoderBitRateKeyIOS?: number; + AVFormatIDKeyIOS?: AVEncodingType; + AVLinearPCMBitDepthKeyIOS?: AVLinearPCMBitDepthKeyIOSType; + AVLinearPCMIsBigEndianKeyIOS?: boolean; + AVLinearPCMIsFloatKeyIOS?: boolean; + AVLinearPCMIsNonInterleavedIOS?: boolean; + AVModeIOS?: AVModeIOSType; + AVNumberOfChannelsKeyIOS?: number; + AVSampleRateKeyIOS?: number; + OutputFormatAndroid?: OutputFormatAndroidType; +} + +export type RNCLIRecordingOptions = { + audioSet?: AudioSet; + /** + * A boolean that determines whether audio level information will be part of the status object under the "metering" key. + */ + isMeteringEnabled?: boolean; +}; + +export type RNCLIAudioRecordingConfiguration = { + options?: RNCLIRecordingOptions; +};