@tokenring-ai/linux-audio
Overview
Linux audio integration using naudiodon3 for Token Ring AI, providing native audio recording and playback capabilities on Linux systems. This plugin integrates with the Token Ring AI ecosystem to enable system-level audio operations within the framework.
The @tokenring-ai/linux-audio package implements the AudioProvider interface for Linux systems, using the naudiodon3 library for native audio I/O operations. It works seamlessly with the @tokenring-ai/audio package's AudioService to provide recording and playback functionality.
Key Features
- Recording: Capture audio from the microphone using naudiodon3 with configurable sample rate and channels
- Playback: Play WAV audio files through system audio, with automatic format conversion via ffmpeg for non-WAV formats
- Format Support: WAV format for recording; supports playback of multiple audio formats (WAV, MP3, etc.) via ffmpeg
- Configurable Options: Customizable sample rate, channels, and format parameters
- Abort Signal Support: Recording can be stopped via AbortSignal with automatic cleanup
- Plugin Integration: Automatically registers with Token Ring AI AudioService when configured
- 16-bit Audio: Records audio in 16-bit PCM format for high quality
- Linux-Only: Designed specifically for Linux systems with ALSA support
Core Components
LinuxAudioProvider
The main class that implements the AudioProvider interface for Linux systems. It handles audio recording and playback operations using naudiodon3 and system-level audio tools.
Implements: AudioProvider
Package Path: @tokenring-ai/linux-audio/LinuxAudioProvider.ts
Usage:
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
Services
LinuxAudioProvider
The main service class that implements the AudioProvider interface for Linux systems.
Constructor
new LinuxAudioProvider(options: LinuxAudioProviderOptions)
Parameters:
options(LinuxAudioProviderOptions): Configuration options including type, record settings, and playback settings
Methods
record(abortSignal: AbortSignal, options?: RecordingOptions): Promise<RecordingResult>
Records audio from the system microphone to a WAV file.
Parameters:
abortSignal(AbortSignal): Signal to stop recordingoptions(RecordingOptions, optional): Recording configuration withsampleRateandchannels
Returns: Promise<RecordingResult> containing the path to the recorded WAV file
Throws: Errors if audio device is unavailable or file cannot be written
Example:
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const abortController = new AbortController();
try {
const recording = await provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
console.log('Recording saved to:', recording.filePath);
} catch (error) {
if (error instanceof Error) {
console.error('Recording failed:', error.message);
}
}
playback(filename: string): Promise<string>
Plays an audio file through the system audio. Supports WAV files directly and other formats via ffmpeg.
Parameters:
filename(string): Path to audio file (WAV or other formats)
Returns: Promise<string> with the filename on success
Throws: Error if file not found or playback fails
Example:
// WAV file - direct playback
await provider.playback('/tmp/recording.wav');
// Other formats (MP3, etc.) via ffmpeg
await provider.playback('/tmp/recording.mp3');
Implementation Details:
- For WAV files: Uses
wav.Readerto parse the file and naudiodon3 for playback - For other formats: Uses
ffmpegto convert to raw PCM and streams to naudiodon3
RecordingResult Interface
interface RecordingResult {
filePath: string;
}
Properties:
filePath(string): Path to the recorded audio file (e.g.,/tmp/recording-2025-01-15T10-30-00-000Z.wav)
Providers
LinuxAudioProvider
The LinuxAudioProvider implements the AudioProvider interface and can be registered with the AudioService.
Configuration Schema: LinuxAudioProviderOptionsSchema
Export Path: @tokenring-ai/linux-audio
Configuration Schema
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const config = LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
});
Configuration Options:
type('linux'): Must be"linux"for this provider (required)record(object): Recording configuration objectsampleRate(number, default:48000): Audio sample rate in Hzchannels(number, default:1): Number of audio channels (1=mono, 2=stereo)format(string, default:'wav'): Audio format for recording
playback(object, default:{}): Playback configuration (currently reserved for future options)
Provider Interface
The LinuxAudioProvider implements the AudioProvider interface from @tokenring-ai/audio:
interface AudioProvider {
record(abortSignal: AbortSignal, options: RecordingOptions): Promise<RecordingResult>;
playback(filename: string): Promise<string>;
}
Plugin Registration
The provider can be registered with AudioService either through the plugin or manually:
import { AudioService } from '@tokenring-ai/audio/AudioService';
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const audioService = new AudioService();
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
// Register with audio service
audioService.registerProvider('linux', provider);
audioService.setActiveProvider('linux');
// Use the provider
const abortController = new AbortController();
const recording = await audioService.record(abortController.signal);
await audioService.playback(recording.filePath);
RPC Endpoints
This package does not define RPC endpoints. Audio operations are handled through the AudioService interface from @tokenring-ai/audio.
Chat Commands
This package does not define chat commands. Chat commands for audio operations are handled by the @tokenring-ai/audio package.
Configuration
Plugin Configuration
The plugin is configured through the Token Ring AI application configuration. When installed, it automatically registers the LinuxAudioProvider with the AudioService.
Configuration Schema:
import { z } from 'zod';
import { AudioServiceConfigSchema } from '@tokenring-ai/audio';
const packageConfigSchema = z.object({
audio: AudioServiceConfigSchema,
});
Example Application Configuration:
{
"audio": {
"providers": {
"linux": {
"type": "linux",
"record": {
"sampleRate": 48000,
"channels": 1,
"format": "wav"
},
"playback": {}
}
}
}
}
Configuration Properties:
audio.providers.linux.type: Must be"linux"audio.providers.linux.record.sampleRate: Audio sample rate in Hz (default: 48000)audio.providers.linux.record.channels: Number of channels (default: 1)audio.providers.linux.record.format: Audio format (default: 'wav')audio.providers.linux.playback: Playback configuration (currently empty)
Configuration Validation
The configuration is validated using Zod schema:
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
// Valid configuration
const validConfig = LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
});
// Invalid configuration will throw
try {
LinuxAudioProviderOptionsSchema.parse({
type: 'macos', // Invalid - must be 'linux'
record: {}
});
} catch (error) {
console.error('Invalid configuration:', error);
}
Integration
The Linux Audio Plugin integrates with the Token Ring AI AudioService to provide native audio operations. It registers as a provider for the AudioService, allowing applications to use it for recording and playback.
Service Integration
import AudioService from '@tokenring-ai/audio/AudioService';
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const audioService = new AudioService();
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
audioService.registerProvider('linux', provider);
audioService.setActiveProvider('linux');
const recording = await audioService.record(abortSignal);
await audioService.playback(recording.filePath);
Plugin Integration
The plugin automatically registers the provider when the application is configured:
Plugin Implementation:
import {TokenRingPlugin} from "@tokenring-ai/app";
import {AudioServiceConfigSchema} from "@tokenring-ai/audio";
import AudioService from "@tokenring-ai/audio/AudioService";
import {z} from "zod";
import LinuxAudioProvider, {LinuxAudioProviderOptionsSchema} from "./LinuxAudioProvider.ts";
const packageConfigSchema = z.object({
audio: AudioServiceConfigSchema,
});
export default {
name: '@tokenring-ai/linux-audio',
version: '0.2.0',
description: 'Linux audio integration using naudiodon3 for Token Ring',
install(app, config) {
if (config.audio) {
app.waitForService(AudioService, audioService => {
for (const name in config.audio!.providers) {
const provider = config.audio!.providers[name];
if (provider.type === "linux") {
audioService.registerProvider(
name,
new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse(provider))
);
}
}
});
}
},
config: packageConfigSchema
} satisfies TokenRingPlugin<typeof packageConfigSchema>;
KeyedRegistry Pattern
The provider uses the KeyedRegistry pattern for provider management within AudioService:
// AudioService maintains a registry of providers
audioService.registerProvider('linux', provider);
// Multiple providers can be registered
audioService.registerProvider('linux', linuxProvider);
audioService.registerProvider('macos', macosProvider);
// Set active provider for operations
audioService.setActiveProvider('linux');
// Get provider by name
const provider = audioService.getProvider('linux');
Usage Examples
Automatic Registration (Recommended)
When used as part of the Token Ring AI application, the plugin automatically registers with the AudioService based on the app configuration:
// Configure in your app config
const config = {
audio: {
providers: {
linux: {
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}
}
}
};
Manual Registration
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { AudioService } from '@tokenring-ai/audio/AudioService';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const audioService = new AudioService();
audioService.registerProvider('linux', provider);
audioService.setActiveProvider('linux');
// Use audio operations
const recording = await audioService.record(abortSignal);
await audioService.playback(recording.filePath);
Recording with Abort Signal
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const abortController = new AbortController();
// Start recording
const recordingPromise = provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
// Stop recording after 10 seconds
setTimeout(() => {
abortController.abort();
}, 10000);
try {
const recording = await recordingPromise;
console.log('Recording saved to:', recording.filePath);
} catch (error) {
if (error instanceof Error) {
console.error('Recording failed or was cancelled:', error.message);
}
}
Playback with Error Handling
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
import fs from 'node:fs';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
// Check file exists before playback
const filename = '/path/to/audio.wav';
if (!fs.existsSync(filename)) {
console.error('Audio file not found:', filename);
process.exit(1);
}
try {
await provider.playback(filename);
console.log('Playback completed');
} catch (error) {
if (error instanceof Error) {
console.error('Playback failed:', error.message);
if (error.message.includes('Audio file not found')) {
console.error('File does not exist');
} else if (error.message.includes('Audio device')) {
console.error('Audio device error - check your audio configuration');
}
}
}
Complete Recording and Playback Workflow
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
async function recordAndPlayback(durationMs: number) {
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const abortController = new AbortController();
try {
// Start recording
const recordingPromise = provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
// Stop recording after specified duration
setTimeout(() => {
abortController.abort();
}, durationMs);
// Wait for recording to complete
const recording = await recordingPromise;
console.log('Recording completed:', recording.filePath);
// Play back the recording
console.log('Starting playback...');
await provider.playback(recording.filePath);
console.log('Playback completed');
} catch (error) {
if (error instanceof Error) {
console.error('Audio operation failed:', error.message);
throw error;
}
}
}
// Record for 5 seconds and play back
await recordAndPlayback(5000);
Best Practices
Recording Best Practices
- Use AbortSignal: Always use AbortSignal to properly stop recording and clean up resources
- Sample Rate Selection: Use 48000 Hz for high-quality audio, 16000 Hz for voice-only applications
- Channel Configuration: Use mono (1 channel) for speech recording to reduce file size
- File Location: Recordings are stored in
/tmp/with timestamp-based filenames (e.g.,recording-2025-01-15T10-30-00-000Z.wav) - File Cleanup: Implement cleanup logic for production use as
/tmp/files may be automatically deleted - Error Handling: Always wrap audio operations in try/catch blocks
Playback Best Practices
- File Validation: Check file existence before playback to avoid errors
- Format Selection: Use WAV format for best compatibility; use ffmpeg for other formats
- Error Handling: Handle both file not found and audio device errors gracefully
- Resource Management: Ensure proper cleanup of audio streams
Performance Optimization
- Sample Rate: Higher sample rates improve audio quality but increase file size
- Channels: Mono channels (1 channel) reduce file size compared to stereo
- Recording Duration: Duration is limited by available disk space in
/tmp/ - AbortSignal: Use AbortSignal to prevent resource leaks and control recording duration
- 16-bit Format: Audio is recorded in 16-bit PCM format for good quality/size balance
Production Considerations
- File Cleanup: Implement periodic cleanup of
/tmp/recordings - Disk Space: Monitor available disk space in
/tmp/ - Audio Device Availability: Handle cases where audio devices are not available
- Permissions: Ensure the application has permission to access audio devices and write to
/tmp/
Error Handling
The provider includes robust error handling for audio operations.
Recording Errors
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const abortController = new AbortController();
try {
const recording = await provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
console.log('Recording saved to:', recording.filePath);
} catch (error) {
if (error instanceof Error) {
console.error('Recording failed:', error.message);
// Handle specific error cases
if (error.message.includes('Audio device not available')) {
console.error('Please check your audio device configuration');
console.error('Run: aplay -l and arecord -l to list audio devices');
} else if (error.message.includes('Permission denied')) {
console.error('Check permissions for /tmp directory');
} else if (error.message.includes('Abort')) {
console.error('Recording was cancelled');
}
}
}
Playback Errors
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
import fs from 'node:fs';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const filename = '/path/to/audio.wav';
// Pre-validate file exists
if (!fs.existsSync(filename)) {
throw new Error(`Audio file not found: ${filename}`);
}
try {
await provider.playback(filename);
console.log('Playback completed');
} catch (error) {
if (error instanceof Error) {
console.error('Playback failed:', error.message);
// Handle file not found
if (error.message.includes('not found')) {
console.error('Audio file does not exist');
}
// Handle audio device errors
if (error.message.includes('Audio device')) {
console.error('Check your audio output device');
console.error('Run: aplay -l to list audio devices');
}
// Handle ffmpeg errors
if (error.message.includes('ffmpeg')) {
console.error('ffmpeg is not installed or failed to convert audio');
console.error('Install ffmpeg: sudo apt-get install ffmpeg');
}
}
}
Cancellation Pattern
Recording can be cancelled using an AbortSignal:
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
const abortController = new AbortController();
// Start recording
const recordingPromise = provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
// Cancel after 3 seconds
setTimeout(() => {
abortController.abort();
}, 3000);
try {
const recording = await recordingPromise;
console.log('Recording completed');
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('Abort') || error.name === 'AbortError') {
console.log('Recording was cancelled');
} else {
console.error('Recording failed:', error.message);
}
}
}
Monitoring and Debugging
Error Types
The provider can throw the following errors:
- General Error:
Errorwith descriptive message for various failure scenarios - File Not Found:
Errorwith message"Audio file not found: <filename>" - Audio Device Errors: Errors from naudiodon3 when devices are unavailable
- FFmpeg Errors: Errors when ffmpeg conversion fails
Debug Logging
Debug logs can be enabled via the Token Ring AI logging system to monitor audio operations. Add logging to track:
- Recording start and stop events
- File paths created
- Playback start and completion
- Error conditions
Performance Monitoring
Monitor the following metrics:
- Recording duration
- File sizes created
- Playback success rate
- Error frequencies
Testing
Unit Tests
The package includes unit tests using Vitest. Run tests with:
# Run tests
bun run test
# Run tests in watch mode
bun run test:watch
# Run tests with coverage
bun run test:coverage
# Build and type check
bun run build
Vitest Configuration:
The package uses the following vitest configuration:
import {defineConfig} from "vitest/config";
export default defineConfig({
test: {
include: ["**/*.test.ts"],
environment: "node",
globals: true,
isolate: true,
},
});
Integration Testing
To test the Linux audio functionality:
import { describe, it, expect, vi, beforeEach } from 'vitest';
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
describe('LinuxAudioProvider', () => {
let provider: LinuxAudioProvider;
beforeEach(() => {
provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
});
it('should create provider with valid options', () => {
expect(provider).toBeDefined();
expect(provider.options.type).toBe('linux');
expect(provider.options.record.sampleRate).toBe(48000);
expect(provider.options.record.channels).toBe(1);
});
it('should have default options', () => {
const providerWithDefaults = new LinuxAudioProvider(
LinuxAudioProviderOptionsSchema.parse({ type: 'linux' })
);
expect(providerWithDefaults.options.record.sampleRate).toBe(48000);
expect(providerWithDefaults.options.record.channels).toBe(1);
expect(providerWithDefaults.options.record.format).toBe('wav');
});
});
Manual Testing
To manually test audio recording and playback:
import LinuxAudioProvider from '@tokenring-ai/linux-audio';
import { LinuxAudioProviderOptionsSchema } from '@tokenring-ai/linux-audio';
async function testAudio() {
const provider = new LinuxAudioProvider(LinuxAudioProviderOptionsSchema.parse({
type: 'linux',
record: {
sampleRate: 48000,
channels: 1,
format: 'wav'
},
playback: {}
}));
console.log('Starting recording for 5 seconds...');
const abortController = new AbortController();
const recordingPromise = provider.record(abortController.signal, {
sampleRate: 48000,
channels: 1
});
setTimeout(() => {
abortController.abort();
}, 5000);
const recording = await recordingPromise;
console.log('Recording saved to:', recording.filePath);
console.log('Starting playback...');
await provider.playback(recording.filePath);
console.log('Playback completed');
}
testAudio().catch(console.error);
Dependencies
Production Dependencies
@tokenring-ai/ai-client: 0.2.0@tokenring-ai/app: 0.2.0@tokenring-ai/agent: 0.2.0@tokenring-ai/audio: 0.2.0@tokenring-ai/chat: 0.2.0@tokenring-ai/naudiodon3: 2.5.0wav: ^1.0.2@types/wav: ^1.0.4zod: ^4.3.6
Development Dependencies
vitest: ^4.1.0typescript: ^5.9.3
Peer Dependencies
@tokenring-ai/audio: Provides theAudioProviderinterface andAudioService
System Requirements
Minimum Requirements
- Linux operating system (Ubuntu/Debian recommended)
- ALSA (Advanced Linux Sound Architecture)
- Node.js 18+ or later
- At least 100MB free disk space for audio recordings
- Audio input device (microphone)
- Audio output device (speakers/headphones)
Installation of System Dependencies
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install libasound2-dev build-essential
# Optional: for non-WAV audio playback
sudo apt-get install ffmpeg
# Verify installation
aplay -l # List playback audio devices
arecord -l # List recording audio devices
# Test audio devices
arecord -f cd -d 5 test.wav # Record 5 seconds
aplay test.wav # Play back
Platform Support
- Linux: Fully supported (Ubuntu, Debian, Fedora tested)
- macOS: Not supported (use
@tokenring-ai/macos-audioinstead) - Windows: Not supported (use
@tokenring-ai/windows-audioinstead)
Development
Building the Package
# Install dependencies
bun install
# Build type definitions
bun run build
# Run tests
bun run test
# Run tests with coverage
bun run test:coverage
Package Structure
pkg/linux-audio/
├── plugin.ts # Plugin definition and registration
├── LinuxAudioProvider.ts # Main provider implementation
├── index.ts # Package exports
├── package.json # Package metadata and dependencies
├── README.md # Package documentation
├── vitest.config.ts # Vitest configuration
└── test/ # Test files
Export Structure
package.json exports:
{
"exports": {
".": "./index.ts",
"./*": "./*.ts"
}
}
Available Exports:
@tokenring-ai/linux-audio: Main package entry point (exportsLinuxAudioProvider)@tokenring-ai/linux-audio/LinuxAudioProvider.ts:LinuxAudioProviderclass andLinuxAudioProviderOptionsSchema
Code Style
- Use TypeScript with strict mode
- Follow ESLint rules from the Token Ring AI project
- Use async/await for asynchronous operations
- Include error handling in all async methods
- Document all public methods with JSDoc comments
Related Components
@tokenring-ai/audio: Audio service and provider interfaces@tokenring-ai/naudiodon3: Native audio I/O for Node.js@tokenring-ai/agent: Agent system integration@tokenring-ai/app: Application frameworkwav: WAV file format support
License
MIT License - see LICENSE file for details.
Contributing
When contributing to this package:
- Ensure all tests pass:
bun run test - Run type checking:
bun run build - Update documentation for any API changes
- Follow the existing code style and patterns
- Test on Linux systems with ALSA
- Add tests for new functionality
Changelog
v0.2.0
- Initial release with Linux audio support
- Recording functionality using naudiodon3
- Audio playback with WAV format support
- Automatic format conversion via ffmpeg for non-WAV files
- Plugin integration with Token Ring AI ecosystem
- 16-bit PCM audio recording
- AbortSignal support for recording cancellation