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

capacitor-audio-bridge

v0.1.1

Published

Capacitor plugin for native audio capture — mono, 24kHz, PCM16

Readme

AudioBridge

Capacitor plugin for native audio capture on iOS and Android.

Captures microphone input and normalises it to mono · 24 kHz · PCM16 little-endian, delivered as a base64 string. No networking, no transcription — raw audio only.


Supported platforms

| Platform | Status | |----------|-----------| | iOS | ✅ M1 complete | | Android | ✅ M1 complete | | Web | 🚫 Stub (throws unimplemented) |


Install

npm install capacitor-audio-bridge
npx cap sync

iOS setup

1. Info.plist — microphone permission

Your host app must declare a microphone usage description or iOS will crash on first permission request.

In ios/App/App/Info.plist add:

<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required to record audio.</string>

2. Podfile

The plugin's podspec declares AVFoundation as a framework dependency. If pod install complains, verify your Podfile includes:

platform :ios, '13.0'

3. Swift version

The plugin requires Swift 5.9 (Xcode 15+). If you see a Swift compatibility error during build, ensure Xcode 15 or later is selected in Xcode → Preferences → Locations → Command Line Tools.


Android setup

1. AndroidManifest.xml

In android/app/src/main/AndroidManifest.xml add inside <manifest>:

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

2. ProGuard / R8

If your release build uses minification, add to android/app/proguard-rules.pro:

-keepclassmembers class * {
    @com.getcapacitor.annotation.PermissionCallback <methods>;
}
-keepclassmembers class * {
    @com.getcapacitor.PluginMethod <methods>;
}

JavaScript API

import { AudioBridge } from 'capacitor-audio-bridge';

// 0. Check permission state without prompting
const { microphone: state } = await AudioBridge.checkPermissions();
// state: 'granted' | 'denied' | 'prompt'

// 1. Request permission (shows OS sheet on first call)
const { microphone } = await AudioBridge.requestPermissions();
if (microphone !== 'granted') throw new Error('Mic denied');

// 2. Start recording
await AudioBridge.startCapture({ sampleRate: 24000, channels: 1 });

// 3. … user speaks …

// 4. Stop and get PCM payload
const result = await AudioBridge.stopCapture();
// result.dataBase64 — base64 PCM16 LE, 24 kHz, mono
// result.durationMs — recording length in milliseconds

// 5. Optional: check state mid-flow
const { value } = await AudioBridge.isCapturing();

StartCaptureOptions

| Field | Type | Default | Notes | |-------|------|---------|-------| | sampleRate | number | 24000 | Output Hz (always resampled to this) | | channels | number | 1 | Output channels (always 1 / mono) | | chunkMs | number | 100 | M2 only — ignored in M1 | | emitChunks | boolean | false | M2 only — ignored in M1 |

StopCaptureResult

| Field | Type | Value | |-------|------|-------| | durationMs | number | Wall-clock length of recording | | sampleRate | number | Always 24000 | | channels | number | Always 1 | | format | string | Always "pcm16" | | dataBase64 | string | Base64-encoded PCM16 LE audio |

Error codes

All rejections include a stable code string as the second argument to reject.

| Code | Thrown by | Meaning | |------|-----------|---------| | PERMISSION_DENIED | startCapture | Mic permission not granted before starting | | ALREADY_CAPTURING | startCapture | A session is already active (iOS + Android) | | INIT_FAILED | startCapture | Native audio recorder failed to initialise (Android) | | START_FAILED | startCapture | Engine start failed (iOS) | | NOT_CAPTURING | stopCapture | Called while no session is active | | STOP_FAILED | stopCapture | Error while stopping or resampling |


iOS — device validation

Prerequisites

  • Physical iPhone (iOS 13+). Simulator uses the Mac microphone and behaves differently.
  • Xcode 15+, pod install completed, app launched with a debug build.

Steps

  1. Add the NSMicrophoneUsageDescription key (see setup above).
  2. Build and deploy to device via Xcode.
  3. Open Xcode → Window → Devices and Simulators → your device → Open Console.
  4. Filter by your app's bundle ID.
  5. Run the JS snippet below in your ODC app.

JS test snippet (iOS)

import { AudioBridge } from 'capacitor-audio-bridge';

async function validateM1() {
  const { microphone } = await AudioBridge.requestPermissions();
  console.assert(microphone === 'granted', 'Permission check failed');

  const { value: beforeStart } = await AudioBridge.isCapturing();
  console.assert(!beforeStart, 'Should not be capturing before start');

  await AudioBridge.startCapture({ sampleRate: 24000, channels: 1 });

  const { value: duringCapture } = await AudioBridge.isCapturing();
  console.assert(duringCapture, 'Should be capturing after start');

  await new Promise(r => setTimeout(r, 3000)); // record for 3 s

  const result = await AudioBridge.stopCapture();

  const { value: afterStop } = await AudioBridge.isCapturing();
  console.assert(!afterStop, 'Should not be capturing after stop');

  console.assert(result.sampleRate === 24000, 'Wrong sample rate');
  console.assert(result.channels === 1,       'Wrong channel count');
  console.assert(result.format === 'pcm16',   'Wrong format');
  console.assert(result.durationMs > 0,       'Zero duration');
  console.assert(result.dataBase64.length > 0,'Empty payload');

  // Decode and check for non-silence
  const raw = atob(result.dataBase64);
  let peak = 0;
  for (let i = 0; i < raw.length - 1; i += 2) {
    const sample = (raw.charCodeAt(i) | (raw.charCodeAt(i + 1) << 8)) << 16 >> 16;
    peak = Math.max(peak, Math.abs(sample));
  }
  console.log(`Peak amplitude: ${peak} (0 = silent / emulator)`);
  console.log(`Duration: ${result.durationMs} ms`);
  console.log('✅ M1 validation passed');
}

validateM1().catch(e => console.error('❌ Validation failed:', e));

Reading logs

Xcode Console will show any Swift-side errors (e.g. engineSetupFailed, permissionDenied). For more detail, use os_log output filtered by subsystem com.audiobridge.


Android — device validation

Prerequisites

  • Physical Android device (API 21+). Emulator mic input is available but the level may be very low.
  • Android Studio or adb logcat available.

Steps

  1. Add RECORD_AUDIO permission to AndroidManifest.xml.
  2. Build a debug APK and install on device.
  3. Run adb logcat -s AudioBridge:D to filter plugin logs.
  4. Run the JS test snippet (same as iOS snippet above — the API is identical).

Reading logs

AudioCaptureManager emits tagged logcat lines on errors. Key tags:

  • AudioBridge/perm — permission state
  • AudioBridge/start — engine start and native sample rate
  • AudioBridge/stop — chunk count, byte count, final duration

Device variance notes

iOS

| Scenario | Behaviour | |----------|-----------| | iPhone native sample rate | Hardware runs at 48 kHz. AVAudioConverter resamples to 24 kHz internally. No manual interpolation needed. | | AirPods / Bluetooth headset | If AirPods disconnect mid-recording, handleRouteChange(.oldDeviceUnavailable) fires; the engine restarts on the built-in mic. Recording continues. | | AirPods codec switch | Triggers AVAudioEngineConfigurationChangeNotification. Plugin reinstalls tap with the new hardware format and restarts. Unlikely during M1 flows. | | Phone call / Siri | interruptionNotification(.began) pauses the engine. On .ended with .shouldResume, recording resumes. If the OS does not set .shouldResume (e.g. Siri redirected audio), the engine stays paused; stopCapture returns partial data. | | iOS Simulator | Uses the Mac's default microphone. Expect low or no amplitude on machines without a mic. | | MDM enterprise restrictions | MDM profiles can block microphone access at the OS level. requestPermissions will return "denied" regardless of the in-app prompt. | | Host app AVAudioSession conflict | If another part of the app sets a different session category, it may conflict. AudioBridge sets .record / .measurement on startCapture. Ensure your host app does not reset the category after that point. |

Android

| Scenario | Behaviour | |----------|-----------| | Native sample rate | AudioRecord probes 24000 → 44100 → 48000 → 16000 Hz. Plugin resamples at stop time using linear interpolation. | | Bluetooth SCO headset | Android may not expose the SCO mic as the default AudioSource.VOICE_RECOGNITION source without additional AudioManager.startBluetoothSco() setup. Not implemented in M1 — falls back to built-in mic. | | Emulator | Microphone is virtualised; amplitude may be near zero or pure silence. Use a physical device for amplitude validation. | | AGC / NS vendor processing | VOICE_RECOGNITION source disables AGC and noise suppression on most devices, but some OEM ROMs (Samsung, Xiaomi) override this. PCM data may still be processed. | | API level < 29 | VOICE_RECOGNITION is available from API 1. No compatibility concerns for M1. |


Payload inspection utility

src/debug.ts contains inspectPayload(result) and logPayloadSummary(result) helpers for local validation. These are not exported from index.ts — import directly during development:

import { inspectPayload, logPayloadSummary } from 'capacitor-audio-bridge/src/debug';

const result = await AudioBridge.stopCapture();
logPayloadSummary(result); // logs peak, RMS, duration drift

Milestone 2 (streaming)

M2 adds live audioChunk events emitted every chunkMs milliseconds. It is not yet implemented.

// Future M2 usage:
const handle = await AudioBridge.addListener('audioChunk', (event) => {
  // event.dataBase64 — PCM16 LE chunk
  // event.sequence   — monotonic chunk index
  // event.timestampMs — ms since capture started
});
await AudioBridge.startCapture({ emitChunks: true, chunkMs: 100 });

Licence

MIT