# tauri-plugin-native-audio
Native mobile audio playback for Tauri applications.
`tauri-plugin-native-audio` gives you one JavaScript API over native playback engines:
- Android: Media3 ExoPlayer + MediaSessionService + notification controls
- iOS: AVPlayer + MPNowPlayingInfoCenter + MPRemoteCommandCenter
It is designed for real mobile playback flows: background-friendly behavior, lock screen / notification controls, and continuous state synchronization in the frontend.
| Linux | - |
| Windows | - |
| macOS | - |
| Android | ✓ |
| iOS | ✓ |
## Install
_This plugin requires a Rust version of at least **1.77.2**._
There are three general methods of installation:
1. crates.io + npm (simplest)
2. Git dependency for Rust + npm package for JS
3. Git/path dependency for both Rust and JS in monorepo/submodule setup
Install the core plugin in `src-tauri/Cargo.toml`:
```toml
[dependencies]
tauri-plugin-native-audio = "1.0.0"
# or from Git:
tauri-plugin-native-audio = { git = "https://github.com/uvarov-frontend/tauri-plugin-native-audio", tag = "v1.0.0" }
```
Install the JavaScript guest bindings:
```sh
pnpm add tauri-plugin-native-audio-api
# or
npm add tauri-plugin-native-audio-api
# or
yarn add tauri-plugin-native-audio-api
```
## Setting up
### 1. Register the plugin in Rust
`src-tauri/src/lib.rs`
```rust
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_native_audio::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
### 2. Enable permissions in capability
Add plugin permissions to your capability file (example: `src-tauri/capabilities/default.json`):
```json
{
"identifier": "default",
"description": "Default capability",
"windows": ["main"],
"permissions": [
"core:default",
"native-audio:default"
]
}
```
`native-audio:default` includes:
- `allow-initialize`
- `allow-register-listener`
- `allow-remove-listener`
- `allow-set-source`
- `allow-play`
- `allow-pause`
- `allow-seek-to`
- `allow-set-rate`
- `allow-get-state`
- `allow-dispose`
If you need stricter ACL, use explicit allow/deny permissions from `permissions/autogenerated/commands/*.toml`.
### 3. Platform notes
Android:
- `play()` starts a foreground service (`NativeAudioService`) for media playback notification.
- On Android 13+ notification permission is requested during `initialize()`.
- Plugin manifest declares:
- `android.permission.INTERNET`
- `android.permission.FOREGROUND_SERVICE`
- `android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK`
- `android.permission.POST_NOTIFICATIONS`
- `android.permission.WAKE_LOCK`
iOS:
- Uses `AVAudioSession` category `.playback`.
- For true background audio, enable Background Modes -> Audio in your iOS target.
- Lock screen controls and now playing metadata are integrated through `MediaPlayer` framework APIs.
## Configuration
This plugin does not require plugin-specific `tauri.conf.json > plugins.native-audio` configuration.
Runtime behavior is configured from JS commands:
- `setSource({ src, title, artist, artworkUrl })`
- `setRate(rate)`
- `seekTo(positionSeconds)`
## Usage
After plugin registration, APIs are available in JavaScript:
```ts
import {
initialize,
setSource,
play,
pause,
seekTo,
setRate,
getState,
dispose,
addStateListener
} from "tauri-plugin-native-audio-api";
const unlisten = await addStateListener((state) => {
console.log("native-audio state", state);
});
await initialize();
await setSource({
src: "https://example.com/audio.mp3",
title: "Episode 1",
artist: "My Podcast",
artworkUrl: "https://example.com/cover.jpg"
});
await setRate(1.25);
await play();
// later
await seekTo(42);
await pause();
console.log(await getState());
unlisten();
await dispose();
```
## JavaScript API
Types:
```ts
export type NativeAudioState = {
status: NativeAudioStatus;
currentTime: number;
duration: number;
isPlaying: boolean;
buffering: boolean;
rate: number;
error?: string;
};
export type NativeAudioSetSourcePayload = {
src: string;
title?: string;
artist?: string;
artworkUrl?: string;
};
```
Commands:
- `initialize(): Promise<NativeAudioState>`
- `setSource(payload: NativeAudioSetSourcePayload): Promise<NativeAudioState>`
- `play(): Promise<NativeAudioState>`
- `pause(): Promise<NativeAudioState>`
- `seekTo(position: number): Promise<NativeAudioState>`
- `setRate(rate: number): Promise<NativeAudioState>`
- `getState(): Promise<NativeAudioState>`
- `dispose(): Promise<void>`
- `addStateListener(handler): Promise<() => void>`
Validation and errors:
- `setSource` rejects if `src` is empty.
- `seekTo` rejects if `position` is missing/non-finite.
- `setRate` rejects if `rate <= 0` or non-finite.
- Native failures can also be reflected in `NativeAudioState.error` with status `error`.
State semantics:
- `idle`: player idle or paused
- `loading`: buffering
- `playing`: active playback
- `ended`: reached media end
- `error`: runtime/player error
## Source handling
Supported source forms:
- remote URL (`https://...`, and platform-dependent `http://...`)
- local file URL (`file://...`)
- local file path
iOS extras:
- `asset://localhost/...` and `http://asset.localhost/...` resolve to local file paths.
- Local files with missing/`.bin` extension are inspected by header and remapped via temporary alias (`mp3`, `wav`, `m4a`, `ogg`) for better AVPlayer compatibility.
## Troubleshooting
`unknown command plugin:native-audio|...`
- Ensure Rust registration exists: `.plugin(tauri_plugin_native_audio::init())`
- Ensure capability includes `native-audio:default` (or explicit allow permissions)
Android notification controls not shown:
- Call `initialize()` before playback
- Start playback using `play()`
- Grant notifications permission on Android 13+
iOS background playback not working:
- Enable Background Modes -> Audio for the iOS app target
- Verify stream URL is reachable and valid
iOS HTTP URL fails:
- ATS can block plain HTTP
- Prefer HTTPS or configure ATS exceptions in app settings
## AI Notice
This plugin was fully developed with the help of AI.
No junior developers were harmed, only a few CPU cores worked overtime 😂
Yes, this duck was also invented by AI 🤣
## License
MIT or Apache-2.0, where applicable.