npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tauri-plugin-audio-recorder-api

v0.1.0

Published

Cross-platform audio recording API for Tauri applications with pause/resume support

Readme

Tauri Plugin Audio Recorder

Cross-platform audio recording plugin for Tauri 2.x applications. Provides audio recording functionality for desktop (Windows, macOS, Linux) and mobile (iOS, Android).

Features

  • Cross-platform: Works on Windows, macOS, Linux, iOS, and Android
  • Desktop WAV / Mobile M4A: Desktop outputs WAV (16-bit PCM). Mobile outputs M4A/AAC due to native API constraints.
  • Quality presets: Low (16kHz mono), Medium (44.1kHz mono), High (48kHz stereo)
  • Pause/Resume: Full control over recording sessions
  • Duration tracking: Real-time duration monitoring
  • Permission handling: Built-in permission request APIs
  • Device enumeration: List available audio input devices

Installation

Rust

Add the plugin to your Cargo.toml:

[dependencies]
tauri-plugin-audio-recorder = "0.1"

TypeScript

Install the JavaScript guest bindings:

npm install tauri-plugin-audio-recorder-api
# or
yarn add tauri-plugin-audio-recorder-api
# or
pnpm add tauri-plugin-audio-recorder-api

Setup

Register Plugin

In your Tauri app setup:

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_audio_recorder::init())
        .run(tauri::generate_context!())
        .expect("error while running application");
}

Permissions

Add permissions to your capabilities/default.json:

{
  "permissions": ["audio-recorder:default"]
}

For granular permissions, you can specify individual commands:

{
  "permissions": [
    "audio-recorder:allow-start-recording",
    "audio-recorder:allow-stop-recording",
    "audio-recorder:allow-pause-recording",
    "audio-recorder:allow-resume-recording",
    "audio-recorder:allow-get-status",
    "audio-recorder:allow-get-devices",
    "audio-recorder:allow-check-permission",
    "audio-recorder:allow-request-permission"
  ]
}

Platform-Specific Setup

Android

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

iOS (Info.plist)

<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record audio for exercises.</string>

Usage

import {
  startRecording,
  stopRecording,
  pauseRecording,
  resumeRecording,
  getStatus,
  getDevices,
  checkPermission,
  requestPermission,
} from "tauri-plugin-audio-recorder-api";

// Check and request permission
const permission = await checkPermission();
if (!permission.granted) {
  const result = await requestPermission();
  if (!result.granted) {
    console.error("Microphone permission denied");
    return;
  }
}

// Start recording
await startRecording({
  outputPath: "/path/to/recording", // without extension
  quality: "medium", // "low" | "medium" | "high"
  maxDuration: 300, // max 5 minutes (0 = unlimited)
});

// Check status
const status = await getStatus();
console.log(`State: ${status.state}, Duration: ${status.durationMs}ms`);

// Pause/Resume
await pauseRecording();
await resumeRecording();

// Stop and get result
const result = await stopRecording();
console.log(`Recorded ${result.durationMs}ms to ${result.filePath}`);
console.log(`File size: ${result.fileSize} bytes`);
console.log(
  `Sample rate: ${result.sampleRate}Hz, Channels: ${result.channels}`
);

Real-time Status Monitoring

import { getStatus } from "tauri-plugin-audio-recorder-api";

// Poll for status updates during recording
const intervalId = setInterval(async () => {
  const status = await getStatus();
  if (status.state === "recording") {
    console.log(`Recording: ${Math.floor(status.durationMs / 1000)}s`);
    updateUI(status.durationMs);
  } else if (status.state === "idle") {
    clearInterval(intervalId);
  }
}, 100); // Update every 100ms

// Stop recording
await stopRecording();
clearInterval(intervalId);

Device Selection

import { getDevices, startRecording } from "tauri-plugin-audio-recorder-api";

// List available devices (Desktop only)
const { devices } = await getDevices();
console.log("Available microphones:");
devices.forEach(device => {
  console.log(`- ${device.name} ${device.isDefault ? "(default)" : ""}`);
});

// Note: Currently uses system default device
// Device selection will be added in future versions

API Reference

Recording Functions

startRecording(config: RecordingConfig): Promise<void>

Start audio recording.

Config:

  • outputPath: File path without extension (required)
  • format: Audio format (currently only "wav", mobile outputs M4A)
  • quality: "low" (16kHz mono) | "medium" (44.1kHz mono) | "high" (48kHz stereo)
  • maxDuration: Max recording duration in seconds (0 = unlimited)

Throws: Error if already recording or permission denied

stopRecording(): Promise<RecordingResult>

Stop recording and get result.

Returns:

  • filePath: Full path to recorded file (with .wav or .m4a extension)
  • durationMs: Recording duration in milliseconds
  • fileSize: File size in bytes
  • sampleRate: Sample rate used (Hz)
  • channels: Number of channels (1 = mono, 2 = stereo)

Throws: Error if not recording

pauseRecording(): Promise<void>

Pause current recording.

Note: Requires Android N+ (API 24+) on Android.

resumeRecording(): Promise<void>

Resume paused recording.

getStatus(): Promise<RecordingStatus>

Get current recording status.

Returns:

  • state: "idle" | "recording" | "paused"
  • durationMs: Current duration in milliseconds
  • outputPath: Output file path (without extension) or null if idle

Utility Functions

getDevices(): Promise<DevicesResponse>

List available audio input devices (Desktop only).

Returns:

  • devices: Array of audio devices with id, name, and isDefault

Note: Returns empty array on mobile (uses system default device).

checkPermission(): Promise<PermissionResponse>

Check microphone permission status.

Returns:

  • granted: Whether permission is granted
  • canRequest: Whether permission can be requested (not permanently denied)

requestPermission(): Promise<PermissionResponse>

Request microphone permission from user.

Returns: Same as checkPermission()

Behavior:

  • Desktop: Returns granted immediately (no permission system)
  • Android: Shows permission dialog
  • iOS: Shows permission dialog on first request

Types

RecordingConfig

interface RecordingConfig {
  outputPath: string; // File path without extension
  format?: "wav"; // Input format option (currently only "wav"; output container varies by platform)
  quality?: "low" | "medium" | "high"; // Quality preset
  maxDuration?: number; // Max duration in seconds (0 = unlimited)
}

RecordingResult

interface RecordingResult {
  filePath: string; // Full path to recorded file
  durationMs: number; // Duration in milliseconds
  fileSize: number; // File size in bytes
  sampleRate: number; // Sample rate used
  channels: number; // Number of channels
}

RecordingStatus

interface RecordingStatus {
  state: "idle" | "recording" | "paused";
  durationMs: number;
  outputPath: string | null;
}

Feature Support Matrix

| Feature | Windows | macOS | Linux | iOS | Android | | ------------------ | ------- | ----- | ----- | --- | ------- | | Recording | ✅ | ✅ | ✅ | ✅ | ✅ | | Pause/Resume | ✅ | ✅ | ✅ | ✅ | ✅* | | Status Monitoring | ✅ | ✅ | ✅ | ✅ | ✅ | | Device Enumeration | ✅ | ✅ | ✅ | ❌ | ❌ | | WAV Output | ✅ | ✅ | ✅ | ⚠️ | ❌ | | M4A Output | ❌ | ❌ | ❌ | ✅ | ✅ | | Max Duration | ✅ | ✅ | ✅ | ✅ | ✅ | | Quality Presets | ✅ | ✅ | ✅ | ✅ | ✅ |

Legend:

  • ✅ Full support
  • ⚠️ iOS can output WAV but defaults to M4A for efficiency
  • ❌ Not supported
  • * Pause/Resume requires Android N+ (API 24+)

Quality Presets

| Preset | Sample Rate | Channels | Use Case | | -------- | ----------- | -------- | ------------------ | | low | 16 kHz | Mono | Voice/Speech | | medium | 44.1 kHz | Mono | General purpose | | high | 48 kHz | Stereo | Music/High quality |

Platform-specific Output Formats

⚠️ Important: Output format varies by platform due to native API constraints.

| Platform | Output Format | Extension | Codec | Notes | | -------- | ------------- | --------- | ----- | ----------------------------------------- | | Desktop | WAV | .wav | PCM | True uncompressed WAV | | Android | M4A | .m4a | AAC | MediaRecorder uses AAC encoder | | iOS | M4A | .m4a | AAC | AVAudioRecorder with kAudioFormatMPEG4AAC |

Handling Format Differences

When processing recordings across platforms, check the file extension or use a media library to detect the format:

import { stopRecording } from "tauri-plugin-audio-recorder-api";

const result = await stopRecording();
const extension = result.filePath.split(".").pop();

if (extension === "wav") {
  // Desktop: PCM WAV - can be processed directly
} else if (extension === "m4a") {
  // Mobile: AAC in M4A container - may need transcoding for some operations
}

Why Different Formats?

  • Desktop: Uses cpal + hound which natively produce WAV
  • Android: MediaRecorder API is optimized for AAC/M4A output; WAV requires lower-level AudioRecord API
  • iOS: AVAudioRecorder can produce WAV but AAC/M4A is more efficient for mobile storage

Converting to Unified Format

If you need consistent format across platforms, use the media-editor plugin to convert:

import { stopRecording } from "tauri-plugin-audio-recorder-api";
import { convert } from "tauri-plugin-media-editor-api";

const result = await stopRecording();

// Convert mobile M4A to WAV if needed
if (result.filePath.endsWith(".m4a")) {
  const converted = await convert({
    inputPath: result.filePath,
    outputPath: result.filePath.replace(".m4a", ""),
    format: "wav",
    audioQuality: "lossless",
  });
  console.log(`Converted to WAV: ${converted.outputPath}`);
}

Troubleshooting

"Permission denied" errors

Always request permission before recording:

const perm = await requestPermission();
if (!perm.granted) {
  if (perm.canRequest) {
    console.error("User denied permission");
  } else {
    console.error("Permission permanently denied. Enable in system settings.");
  }
  return;
}

iOS: Ensure NSMicrophoneUsageDescription is in Info.plist

Android: Ensure RECORD_AUDIO permission is in AndroidManifest.xml

"Already recording" errors

Solution: Stop current recording before starting new one:

const status = await getStatus();
if (status.state !== "idle") {
  await stopRecording();
}
await startRecording(config);

Pause/Resume not working on Android

Issue: Pause/Resume requires Android N+ (API 24+, Android 7.0+)

Solution: Check Android version or handle gracefully:

try {
  await pauseRecording();
} catch (error) {
  if (String(error).includes("not supported")) {
    console.warn("Pause not supported on this Android version");
    // Fallback: stop and restart recording
  }
}

No audio devices found (Desktop)

Checklist:

  • ✅ Microphone is connected
  • ✅ Microphone is enabled in system settings
  • ✅ Microphone is not exclusively used by another app
  • ✅ Audio drivers are up to date

Debug:

const { devices } = await getDevices();
if (devices.length === 0) {
  console.error("No audio input devices found");
  console.log("Check system audio settings");
}

Recording file is empty or corrupted

Common causes:

  • Recording stopped immediately after start
  • Insufficient disk space
  • File path not writable
  • Microphone not working

Solution: Add minimum duration check:

const result = await stopRecording();
if (result.durationMs < 100) {
  console.warn("Recording too short, may be corrupted");
}
if (result.fileSize < 1000) {
  console.warn("File size too small, recording may have failed");
}

Max duration not triggering

Note: maxDuration is a hard limit. Recording stops automatically when reached, but you won't receive a callback.

Recommended pattern:

await startRecording({
  outputPath: "/path/to/recording",
  maxDuration: 60, // 60 seconds
});

// Poll status to detect when stopped
const checkInterval = setInterval(async () => {
  const status = await getStatus();
  if (status.state === "idle") {
    clearInterval(checkInterval);
    const result = await stopRecording();
    console.log("Recording completed:", result);
  }
}, 1000);

Format conversion between platforms

Problem: Desktop produces WAV, mobile produces M4A.

Solution: Use Media Toolkit plugin for conversion:

import { stopRecording } from "tauri-plugin-audio-recorder-api";
import { convert } from "tauri-plugin-media-toolkit-api";

const result = await stopRecording();

if (result.filePath.endsWith(".m4a")) {
  // Convert mobile M4A to WAV
  const converted = await convert({
    inputPath: result.filePath,
    outputPath: result.filePath.replace(".m4a", ""),
    format: "wav",
    audioQuality: "lossless",
  });
  console.log("Converted to WAV:", converted.outputPath);
}

Output path doesn't include extension

This is by design. The plugin automatically appends the correct extension:

// Input: outputPath = "/path/to/recording"
// Desktop output: "/path/to/recording.wav"
// Mobile output: "/path/to/recording.m4a"

// ❌ Don't include extension:
outputPath: "/path/to/recording.wav"; // Wrong

// ✅ Correct:
outputPath: "/path/to/recording"; // Correct

Platform Notes

Desktop

  • Uses cpal for cross-platform audio input
  • Uses hound for WAV encoding
  • Full device enumeration support

Android

  • Uses MediaRecorder API
  • Requires RECORD_AUDIO permission
  • Pause/Resume requires Android N (API 24+)

iOS

  • Uses AVAudioRecorder
  • Requires NSMicrophoneUsageDescription in Info.plist
  • True WAV output with Linear PCM

Examples

See the examples/audio-recorder-example directory for a complete working demo with React + Material UI, featuring:

  • Permission handling flow
  • Recording with pause/resume
  • Real-time duration display
  • Quality preset selection
  • Device enumeration (desktop)
  • Status monitoring
  • Error handling

License

MIT