Audiocraft - Text-to-Speech Audio File Converter

A powerful Flutter plugin for converting text to audio files with background processing support. This package allows you to convert text into audio files that can be played back later, perfect for audiobook apps, reading assistants, and content accessibility features.

Features

Text-to-Speech to File: Convert any text to audio files (WAV format)
Background Processing: Convert audio without blocking the UI
Chunking Support: Automatically split long text into manageable chunks
Audio Merging: Seamlessly merge multiple audio chunks
Local Notifications: Get notified when conversion is complete
Cache Management: Intelligent caching and storage management
Cross-Platform: Full Android and iOS support
Demo Mode: Test functionality without native implementation

Getting Started

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  audiocraft: ^0.0.1

Permissions

Android (android/app/src/main/AndroidManifest.xml):

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

iOS (ios/Runner/Info.plist):

<key>NSMicrophoneUsageDescription</key>
<string>App cần quyền micro để tạo file audio từ text-to-speech</string>
<key>NSDocumentsDirectoryUsageDescription</key>
<string>App cần quyền lưu file audio vào thư mục tài liệu</string>

Usage

Basic Implementation

import 'package:audiocraft/audiocraft.dart';

class AudioExample extends StatefulWidget {
  @override
  _AudioExampleState createState() => _AudioExampleState();
}

class _AudioExampleState extends State<AudioExample> {
  final AudioManagerService _audioManager = AudioManagerService();

  @override
  void initState() {
    super.initState();
    _initializeAudio();
  }

  Future<void> _initializeAudio() async {
    await _audioManager.initialize();
    
    // Setup callbacks
    _audioManager.onConversionProgress = (progress) {
      print('Conversion: ${(progress.progress * 100).round()}% - ${progress.message}');
    };
    
    _audioManager.onPlaybackProgress = (progress) {
      print('Playing: ${progress.position} / ${progress.duration}');
    };
    
    _audioManager.onStateChanged = (state) {
      print('State: $state');
    };
  }

  Future<void> _convertAndPlay() async {
    // Load and convert text to audio
    await _audioManager.loadChapter(
      chapterId: 'chapter_1',
      chapterContent: 'Hello, this is a test of the text-to-speech system.',
    );
    
    // Play the audio
    await _audioManager.play();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Audiocraft Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _convertAndPlay,
              child: Text('Convert & Play'),
            ),
            ElevatedButton(
              onPressed: () => _audioManager.pause(),
              child: Text('Pause'),
            ),
            ElevatedButton(
              onPressed: () => _audioManager.setSpeed(1.5),
              child: Text('Speed 1.5x'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _audioManager.dispose();
    super.dispose();
  }
}

Advanced Configuration

// Custom audio configuration
final config = AudioConversionConfig(
  speed: 1.2,
  pitch: 1.1,
  volume: 0.8,
  language: 'en-US',
  maxChunkLength: 3000,
  audioFormat: 'wav',
);

await _audioManager.loadChapter(
  chapterId: 'chapter_1',
  chapterContent: longText,
  config: config,
);

Cache Management

// Check cache size
final cacheSize = await _audioManager.getCacheSize();
print('Cache size: ${_audioManager.formatCacheSize(cacheSize)}');

// Clear all cache
await _audioManager.clearAllCache();

// Clear specific chapter
await _audioManager.clearChapterCache('chapter_1');

API Reference

AudioManagerService

Main service for managing audio conversion and playback.

Methods:

  • initialize() - Initialize the service
  • loadChapter({chapterId, chapterContent, config}) - Load and convert text
  • play() - Start playback
  • pause() - Pause playback
  • stop() - Stop playback
  • seek(Duration position) - Seek to position
  • setSpeed(double speed) - Set playback speed
  • setVolume(double volume) - Set volume

Callbacks:

  • onConversionProgress - Conversion progress updates
  • onPlaybackProgress - Playback progress updates
  • onStateChanged - Audio state changes
  • onError - Error notifications
  • onChapterCompleted - Chapter playback completed

BackgroundAudioConverter

Static service for direct text-to-audio conversion.

Methods:

  • convertTextToAudio({text, chapterId, config, onProgress}) - Convert text
  • hasAudioFile(chapterId) - Check if audio exists
  • getAudioFile(chapterId) - Get audio file
  • deleteAudioFile(chapterId) - Delete audio file
  • clearAllCache() - Clear all cached files

Demo Mode vs Production

The package includes a demo mode for testing without native implementation:

// In lib/services/background_audio_converter.dart
static const bool USE_DEMO_MODE = true; // Change to false for production

Demo Mode: Creates dummy WAV files with sine wave audio
Production Mode: Uses native TTS engines for real audio conversion

Performance Notes

  • File Size: ~200KB per 1000 characters in demo mode
  • Conversion Time: ~0.5s + 10ms per character in demo mode
  • Chunk Size: Default 4000 characters (Android TTS limitation)
  • Cache Location: <App Documents>/audio_cache/

Troubleshooting

Common Issues

  1. Conversion Failed

    • Check permissions (microphone, storage)
    • Verify available storage space
    • Check text encoding and length
  2. Playback Issues

    • Ensure audio file exists
    • Check audio format support
    • Verify audio session configuration
  3. Performance Issues

    • Enable caching for repeated content
    • Optimize chunk size for your content
    • Use background processing for long texts

Debug Information

// Enable debug logging
print('State: ${_audioManager.currentState}');
print('File exists: ${await audioFile.exists()}');
print('Cache size: ${await _audioManager.getCacheSize()}');

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.