capacitor-audio-engine
Version:
High-quality audio recording Capacitor plugin with native iOS & Android support. Features pause/resume, microphone management, real-time monitoring, audio trimming, and comprehensive mobile audio recording capabilities.
471 lines (470 loc) • 18.5 kB
TypeScript
import type { PluginListenerHandle } from '@capacitor/core';
export declare type RecordingStatus = 'idle' | 'recording' | 'paused';
export declare type PlaybackStatus = 'idle' | 'loading' | 'playing' | 'paused' | 'stopped';
export declare type AudioRecordingEventName = 'durationChange' | 'error';
export declare type AudioPlaybackEventName = 'trackChanged' | 'trackEnded' | 'playbackStarted' | 'playbackPaused' | 'playbackError' | 'playbackProgress' | 'playbackStatusChanged';
export declare type AudioEventName = AudioRecordingEventName | AudioPlaybackEventName;
export interface AudioRecordingEvent<T = any> {
eventName: AudioRecordingEventName;
payload: T;
}
export interface AudioPlaybackEvent<T = any> {
eventName: AudioPlaybackEventName;
payload: T;
}
export declare type AudioRecordingEventMap = {
durationChange: DurationChangeData;
error: ErrorEventData;
};
export declare type AudioPlaybackEventMap = {
trackChanged: TrackChangedData;
trackEnded: TrackEndedData;
playbackStarted: PlaybackStartedData;
playbackPaused: PlaybackPausedData;
playbackError: ErrorEventData;
playbackProgress: PlaybackProgressData;
playbackStatusChanged: PlaybackStatusChangedData;
};
export declare type AudioEventMap = AudioRecordingEventMap & AudioPlaybackEventMap;
export interface DurationChangeData {
duration: number;
}
export interface ErrorEventData {
message: string;
code?: string | number;
details?: any;
}
export interface AudioTrack {
id: string;
url: string;
title?: string;
artist?: string;
artworkUrl?: string;
}
export interface TrackChangedData {
track: AudioTrack;
index: number;
}
export interface TrackEndedData {
track: AudioTrack;
index: number;
}
export interface PlaybackStartedData {
track: AudioTrack;
index: number;
}
export interface PlaybackPausedData {
track: AudioTrack;
index: number;
position: number;
}
export interface PlaybackProgressData {
track: AudioTrack;
index: number;
currentPosition: number;
duration: number;
isPlaying: boolean;
}
export interface PlaybackStatusChangedData {
track: AudioTrack | null;
index: number;
status: PlaybackStatus;
currentPosition: number;
duration: number;
isPlaying: boolean;
}
export interface RecordingOptions {
/**
* Audio sample rate (Hz). Default: 22050 (optimized for smaller file sizes)
*/
sampleRate?: number;
/**
* Number of audio channels. Default: 1 (mono)
*/
channels?: number;
/**
* Audio bitrate (bps). Default: 64000 (optimized for smaller file sizes)
*/
bitrate?: number;
/**
* Audio quality preset. If specified, overrides individual sampleRate and bitrate settings.
* - 'low': 16kHz, 32kbps - smallest files, suitable for voice notes
* - 'medium': 22.05kHz, 64kbps - balanced quality/size (default)
* - 'high': 44.1kHz, 128kbps - higher quality, larger files
*/
quality?: 'low' | 'medium' | 'high';
/**
* Maximum recording duration in seconds.
* When set, enables segment rolling mode:
* - Records in 30-second segments
* - Maintains rolling buffer of last 10 minutes (20 segments)
* - Automatically merges segments when recording stops
* If not set, uses linear recording mode.
*/
maxDuration?: number;
}
export interface AudioFileInfo {
path: string;
webPath: string;
uri: string;
mimeType: string;
size: number;
duration: number;
sampleRate: number;
channels: number;
bitrate: number;
createdAt: number;
filename: string;
/**
* Base64-encoded audio data with MIME prefix (Data URI format)
*
* iOS may return compressed data for better performance:
* - Compressed: "data:audio/m4a;base64,lzfse:<compressed-base64-data>"
* - Uncompressed: "data:audio/m4a;base64,<base64-data>"
*
* Use compression-utils.ts helpers to parse and handle compressed data.
* Compression is lossless LZFSE algorithm optimized for iOS.
*/
base64?: string;
}
export interface MicrophoneInfo {
id: number;
name: string;
type: 'internal' | 'external' | 'unknown';
description?: string;
uid?: string;
isConnected?: boolean;
}
export interface MicrophoneStatusResult {
busy: boolean;
reason?: string;
}
export interface AvailableMicrophonesResult {
microphones: MicrophoneInfo[];
}
export interface SwitchMicrophoneOptions {
microphoneId: number;
}
export interface SwitchMicrophoneResult {
success: boolean;
microphoneId: number;
}
export interface PreloadTracksOptions {
tracks: string[];
preloadNext?: boolean;
}
export interface PreloadedTrackInfo {
url: string;
loaded: boolean;
mimeType?: string;
duration?: number;
size?: number;
}
export interface PreloadTracksResult {
tracks: PreloadedTrackInfo[];
}
export interface PlaybackInfo {
currentTrack: AudioTrack | null;
currentIndex: number;
currentPosition: number;
duration: number;
isPlaying: boolean;
status: PlaybackStatus;
}
export interface SeekOptions {
seconds: number;
url?: string;
}
export interface PlayAudioOptions {
url?: string;
}
export interface PauseAudioOptions {
url?: string;
}
export interface ResumeAudioOptions {
url?: string;
}
export interface StopAudioOptions {
url?: string;
}
export interface SkipToIndexOptions {
index: number;
}
/**
* Interface for the Native Audio Plugin that provides audio recording capabilities.
*
* Platform-specific implementations:
* - Web: Uses MediaRecorder API with WebM/Opus format for recording
* - Android: Uses MediaRecorder with AAC format in MP4 container for recording
* - iOS: Uses AVAudioRecorder with AAC format in M4A container for recording
*
* Common settings across platforms:
* - Sample Rate: 44.1kHz
* - Channels: 1 (mono)
* - Bitrate: 128kbps
*/
export interface CapacitorAudioEnginePlugin {
/**
* Test method to verify plugin functionality.
* @param options - Echo options
* @param options.value - String value to echo back
* @returns Promise that resolves with the echoed value
*/
echo(options: {
value: string;
}): Promise<{
value: string;
}>;
/**
* Check if the app has microphone permission.
* @returns Promise that resolves with an object containing the permission status
* @property {boolean} granted - Whether microphone permission is granted
* @property {boolean} audioPermission - Whether audio permission is granted
* @property {boolean} notificationPermission - Whether notification permission is granted (Android 13+ only)
* @platform web Uses navigator.permissions.query API
* @platform android Uses ContextCompat.checkSelfPermission with RECORD_AUDIO permission
* @platform ios Uses AVAudioSession.recordPermission
*/
checkPermission(): Promise<{
granted: boolean;
audioPermission?: boolean;
notificationPermission?: boolean;
}>;
/**
* Request microphone permission from the user.
* @returns Promise that resolves with an object containing the permission status
* @property {boolean} granted - Whether microphone permission was granted
* @property {boolean} audioPermission - Whether audio permission was granted
* @property {boolean} notificationPermission - Whether notification permission was granted (Android 13+ only)
* @platform web Uses navigator.mediaDevices.getUserMedia API
* @platform android Uses ActivityCompat.requestPermissions with RECORD_AUDIO permission
* @platform ios Uses AVAudioSession.requestRecordPermission
*/
requestPermission(): Promise<{
granted: boolean;
audioPermission?: boolean;
notificationPermission?: boolean;
}>;
/**
* Start recording audio from the device's microphone.
* @param options - Recording options
* @returns Promise that resolves when recording starts successfully
* @throws {Error} If recording is already in progress
* @throws {Error} If microphone permission is not granted
* @throws {Error} If audio session setup fails
* @platform web Uses MediaRecorder API
* @platform android Uses MediaRecorder
* @platform ios Uses AVAudioRecorder
*/
startRecording(options?: RecordingOptions): Promise<void>;
/**
* Pause the current recording.
* @returns Promise that resolves when recording is paused successfully
* @throws {Error} If no active recording exists or if recording is already paused
* @platform web Uses MediaRecorder.pause()
* @platform android Uses MediaRecorder.pause() (Android N/API 24+ only)
* @platform ios Uses AVAudioRecorder.pause()
*/
pauseRecording(): Promise<void>;
/**
* Resume the current recording if it was previously paused.
* @returns Promise that resolves when recording is resumed successfully
* @throws {Error} If no active recording exists or if recording is not paused
* @platform web Uses MediaRecorder.resume()
* @platform android Uses MediaRecorder.resume() (Android N/API 24+ only)
* @platform ios Uses AVAudioRecorder.record()
*/
resumeRecording(): Promise<void>;
/**
* Reset the current recording session by pausing, deleting previous segments,
* and resetting all counters. After calling this method, resumeRecording()
* will behave like starting a fresh recording session.
* @returns Promise that resolves when recording is reset successfully
* @throws {Error} If no active recording exists
* @platform web Resets MediaRecorder state and clears recorded data
* @platform android Stops segment rolling, deletes segments, resets duration monitoring
* @platform ios Stops segment rolling, deletes segments, resets AVAudioRecorder state
*/
resetRecording(): Promise<void>;
/**
* Stop the current recording and get the recorded file information.
* @returns Promise that resolves with the recorded audio file details
* @throws {Error} If no active recording exists
* @throws {Error} If file processing fails
*/
stopRecording(): Promise<AudioFileInfo>;
/**
* Get the current recording duration.
* @returns Promise that resolves with the current duration in seconds
* @throws {Error} If no active recording exists
* @property {number} duration - Recording duration in seconds
* @platform web Not supported - returns 0
* @platform android Uses MediaRecorder.getMaxAmplitude()
* @platform ios Uses AVAudioRecorder.currentTime
*/
getDuration(): Promise<{
duration: number;
}>;
/**
* Get the current recording status.
* @returns Promise that resolves with the current recording status
* @property {RecordingStatus} status - The current state of the recorder
* @property {boolean} isRecording - True if the recording session is active
* @property {number} duration - The current recording duration in seconds
*/
getStatus(): Promise<{
status: RecordingStatus;
isRecording: boolean;
duration: number;
}>;
/**
* Trim an audio file to the specified start and end times.
* @param options - Trim options
* @param options.uri - URI of the audio file to trim
* @param options.start - Start time in seconds
* @param options.end - End time in seconds
* @returns Promise that resolves with the trimmed audio file details
* @throws {Error} If file processing fails
* @platform web Not supported
* @platform android Uses MediaExtractor and MediaMuxer
* @platform ios Uses AVAssetExportSession
*/
trimAudio(options: {
uri: string;
start: number;
end: number;
}): Promise<AudioFileInfo>;
/**
* Add a listener for recording events
* @param eventName - The name of the event to listen to
* @param callback - The callback to invoke when the event occurs
* @returns A promise that resolves with a handle to the listener
* @platform web Not supported, recording events use MediaRecorder
* @platform android Uses MediaRecorder events
* @platform ios Uses AVAudioSession notifications
*
* @example
* ```typescript
* // Listen for playback progress updates (every 500ms)
* const progressListener = await CapacitorAudioEngine.addListener('playbackProgress', (data) => {
* console.log(`Progress: ${data.currentPosition}/${data.duration} seconds`);
* });
*
* // Listen for status changes
* const statusListener = await CapacitorAudioEngine.addListener('playbackStatusChanged', (data) => {
* console.log(`Status: ${data.status}, Playing: ${data.isPlaying}`);
* });
*
* // Don't forget to remove listeners when done
* progressListener.remove();
* statusListener.remove();
* ```
*/
addListener<T extends AudioEventName>(eventName: T, callback: (event: AudioEventMap[T]) => void): Promise<PluginListenerHandle>;
/**
* Remove all listeners
* @returns Promise that resolves when all listeners are removed
*/
removeAllListeners(): Promise<void>;
/**
* Check if microphone is currently being used by another application.
* @returns Promise that resolves with microphone status
* @property {boolean} busy - Whether microphone is currently in use
* @platform web Not supported
* @platform android Uses AudioRecord to test microphone availability
* @platform ios Uses AVAudioSession to check audio state
*/
isMicrophoneBusy(): Promise<MicrophoneStatusResult>;
/**
* Get list of available microphones (internal and external).
* @returns Promise that resolves with available microphones
* @property {MicrophoneInfo[]} microphones - Array of available microphones
* @platform web Not supported
* @platform android Uses AudioManager.getDevices() to enumerate audio inputs
* @platform ios Uses AVAudioSession.availableInputs to list audio inputs
*/
getAvailableMicrophones(): Promise<AvailableMicrophonesResult>;
/**
* Switch between microphones while keeping recording active.
* @param options - Switch microphone options
* @param options.microphoneId - ID of the microphone to switch to
* @returns Promise that resolves with switch result
* @throws {Error} If microphone ID is invalid
* @throws {Error} If switching fails
* @platform web Not supported
* @platform android Uses AudioRecord.setPreferredDevice() to switch input
* @platform ios Uses AVAudioSession.setPreferredInput() to switch input
*/
switchMicrophone(options: SwitchMicrophoneOptions): Promise<SwitchMicrophoneResult>;
/**
* Preload audio tracks from URLs and initialize playlist
* @param options - Preload options containing track URLs and preload settings
* @returns Promise that resolves with preload results for each track including load status, mimetype, duration, and file size
* @platform web Uses HTML5 Audio API
* @platform android Uses ExoPlayer with ConcatenatingMediaSource
* @platform ios Uses AVQueuePlayer or AVPlayer with queue management
*/
preloadTracks(options: PreloadTracksOptions): Promise<PreloadTracksResult>;
/**
* Start or resume playback of current track or specific preloaded track by URL
* @param options - Optional playback options with URL to play specific preloaded track
* @returns Promise that resolves when playback starts
*/
playAudio(options?: PlayAudioOptions): Promise<void>;
/**
* Pause audio playback for current track or specific preloaded track by URL
* @param options - Optional pause options with URL to pause specific preloaded track
* @returns Promise that resolves when playback is paused
*/
pauseAudio(options?: PauseAudioOptions): Promise<void>;
/**
* Resume audio playback from paused state for current track or specific preloaded track by URL
* @param options - Optional resume options with URL to resume specific preloaded track
* @returns Promise that resolves when playback resumes
*/
resumeAudio(options?: ResumeAudioOptions): Promise<void>;
/**
* Stop audio playback and reset to beginning for current track or specific preloaded track by URL
* @param options - Optional stop options with URL to stop specific preloaded track
* @returns Promise that resolves when playback stops
*/
stopAudio(options?: StopAudioOptions): Promise<void>;
/**
* Seek to specific position in current track or specific preloaded track by URL
* @param options - Seek options with time in seconds and optional URL for specific preloaded track
* @returns Promise that resolves when seek completes
*/
seekAudio(options: SeekOptions): Promise<void>;
/**
* Skip to next track in playlist
* @returns Promise that resolves when skip completes
*/
skipToNext(): Promise<void>;
/**
* Skip to previous track in playlist
* @returns Promise that resolves when skip completes
*/
skipToPrevious(): Promise<void>;
/**
* Skip to specific track index in playlist
* @param options - Options with target track index
* @returns Promise that resolves when skip completes
*/
skipToIndex(options: SkipToIndexOptions): Promise<void>;
/**
* Get current playback information
* @returns Promise that resolves with current playback state
*/
getPlaybackInfo(): Promise<PlaybackInfo>;
/**
* Navigate to the app's permission settings screen.
* This method opens the system settings page where users can manually adjust permissions.
* Useful when permissions are denied and need to be changed through settings.
* @returns Promise that resolves when navigation is initiated
* @throws {Error} If navigation fails
* @platform web Not supported - shows alert with instructions
* @platform android Opens app permissions section using ACTION_APPLICATION_DETAILS_SETTINGS intent, attempts direct permissions page first
* @platform ios Opens app settings using UIApplication.openSettingsURLString (iOS directs to app-specific settings)
*/
openAppSettings(): Promise<void>;
}