# How FluidVoice Manages Audio History Storage: Budget Controls and ZIP Export

> Learn how FluidVoice manages audio history storage. Discover budget controls for automatic cleanup and ZIP export options for your dictations. Optimize your storage efficiently.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: how-to-guide
- Published: 2026-07-01

---

**FluidVoice stores every dictation as a WAV file in the macOS Application Support directory, enforces a user-defined storage budget with automatic cleanup of oldest files, and exports selectable entries as ZIP archives containing a JSONL manifest.**

FluidVoice, an open-source dictation application maintained by altic-dev, implements a complete audio history management system that balances persistent storage with disk space constraints. The **FluidVoice audio history storage** system relies on three tightly-coupled components: `DictationAudioHistoryStore` for file operations, `SettingsStore` for budget persistence, and `SettingsView` for user interaction.

## Audio File Storage Location

Audio files are persisted in a dedicated subdirectory of the user's Application Support folder. In [`Sources/Fluid/Persistence/DictationAudioHistoryStore.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/DictationAudioHistoryStore.swift), the private `audioDirectory(createIfNeeded:)` method constructs this path:

```swift
private func audioDirectory(createIfNeeded: Bool = true) throws -> URL {
    guard let base = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
        throw DictationAudioHistoryError.applicationSupportUnavailable
    }
    let directory = base
        .appendingPathComponent("FluidVoice", isDirectory: true)
        .appendingPathComponent("DictationAudioHistory", isDirectory: true)
    if createIfNeeded {
        try fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
    }
    return directory
}

```

When transcription completes, the `save(snapshot:entryID:timestamp:model:)` method writes a WAV file into this directory and returns a `DictationAudioMetadata` struct containing the file name, size, sample rate, and channel count. The system maintains each recording as a separate uncompressed WAV file for maximum compatibility and quality retention.

## Storage Budget Controls and Enforcement

Users configure storage limits through the Settings UI, backed by `SettingsStore` in [`Sources/Fluid/Persistence/SettingsStore.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/SettingsStore.swift). The budget is stored in gigabytes but exposed as bytes for enforcement:

```swift
var audioHistoryBudgetGB: Double {
    get {
        let value = defaults.double(forKey: Keys.audioHistoryBudgetGB)
        return value > 0 ? max(0.1, value) : 4.0      // default = 4 GB, minimum = 0.1 GB
    }
    set {
        objectWillChange.send()
        defaults.set(max(0.1, newValue), forKey: Keys.audioHistoryBudgetGB)
    }
}
var audioHistoryBudgetBytes: Int64 {
    DictationAudioHistoryStore.bytes(forGigabytes: self.audioHistoryBudgetGB)
}

```

The `SettingsView` retrieves current usage via `DictationAudioHistoryStore.audioUsageBytes()` and displays a progress indicator comparing usage against `SettingsStore.shared.audioHistoryBudgetBytes`. When a user reduces the budget below current usage and taps **Apply**, the `applyAudioHistoryBudget()` method updates the setting. The next time `TranscriptionHistoryStore` performs cleanup, it invokes `DictationAudioHistoryStore.deleteUnreferencedAudioFiles` to evict the oldest audio files until the total size complies with the new budget.

## Exporting ZIP Archives with JSONL Manifests

The **Export ZIP** functionality in `SettingsView` triggers `exportAudioZip()`, which delegates to `DictationAudioHistoryStore.exportAudioArchive(entries:to:)`. This implementation follows a strict staging process:

1. **Validation**: Accepts an array of `TranscriptionHistoryEntry` objects, skipping any where `audioFileExists(for:)` returns false.
2. **Staging**: Creates a temporary directory and copies selected WAV files into a `staging/audio/` subdirectory using deterministic filenames formatted as `yyyy-MM-dd'T'HH-mm-ssZ_<uuid>.wav`.
3. **Manifest Generation**: Writes a `manifest.jsonl` file (line-delimited JSON) using `AudioManifestRow` structs encoded via `JSONEncoder`. Each line maps the audio filename to its transcript, timestamp, duration, technical metadata, and model information.
4. **Compression**: Executes `/usr/bin/zip` with arguments `-qr`, the destination path, `manifest.jsonl`, and the `audio` directory. Errors are captured and re-thrown as `DictationAudioHistoryError.zipFailed`.

The resulting archive is saved to the user-selected location (e.g., Desktop) and can be extracted by any standard archive utility for downstream analysis or backup.

## Deleting Audio History

For immediate cleanup, `SettingsView` provides a **Delete Audio** button that invokes `deleteSavedAudio()`. This method calls `DictationAudioHistoryStore.deleteAllAudioFiles()`, which removes every `.wav` file from the `DictationAudioHistory` directory without affecting transcription metadata stored elsewhere.

## Swift Code Examples

### Query Current Usage and Budget

```swift
import Fluid

let usageBytes = DictationAudioHistoryStore.shared.audioUsageBytes()
let usageGB = Double(usageBytes) / 1_073_741_824.0
let budgetGB = SettingsStore.shared.audioHistoryBudgetGB

print("Audio usage: \(String(format: "%.2f", usageGB)) GB")
print("Budget: \(budgetGB) GB")

```

This produces the same metrics displayed in the Settings UI progress bar.

### Adjust Storage Budget

```swift
SettingsStore.shared.audioHistoryBudgetGB = 2.0

```

The store automatically converts this to bytes. The next history trimming cycle will delete oldest files if the new limit is exceeded.

### Export Selected Entries to ZIP

```swift
import Fluid

let entries = TranscriptionHistoryStore.shared.allEntries
let destination = URL(fileURLWithPath: "/Users/me/Desktop/FluidVoice_Export.zip")

do {
    try DictationAudioHistoryStore.shared.exportAudioArchive(
        entries: entries,
        to: destination
    )
    print("Export succeeded: \(destination.path)")
} catch {
    print("Export failed: \(error)")
}

```

The output contains `manifest.jsonl` and an `audio/` folder with deterministically named WAV files.

### Delete All Audio Files

```swift
DictationAudioHistoryStore.shared.deleteAllAudioFiles()
print("Audio history cleared")

```

## Summary

- FluidVoice stores unlimited dictation audio as individual WAV files in `~/Library/Application Support/FluidVoice/DictationAudioHistory/` (managed by `DictationAudioHistoryStore`).
- **Budget enforcement** defaults to 4 GB with a 0.1 GB minimum, persisted via `SettingsStore` and enforced by `TranscriptionHistoryStore` during cleanup cycles.
- **ZIP exports** create self-contained archives with a `manifest.jsonl` file linking audio filenames to transcript metadata, generated via temporary staging and `/usr/bin/zip`.
- **Deletion** can be performed per-entry or globally via `deleteAllAudioFiles()`, immediately reclaiming disk space.

## Frequently Asked Questions

### Where does FluidVoice store audio files on macOS?

Audio files reside in the `DictationAudioHistory` subdirectory of the app's Application Support folder, typically located at `~/Library/Application Support/FluidVoice/DictationAudioHistory/`. This path is constructed programmatically by `DictationAudioHistoryStore.audioDirectory()` using `FileManager` URLs for the `.applicationSupportDirectory`.

### What triggers automatic deletion when the storage budget is exceeded?

When you set a new budget lower than current usage in **Settings**, the value is saved immediately but files are not deleted synchronously. Instead, `TranscriptionHistoryStore` calls `DictationAudioHistoryStore.deleteUnreferencedAudioFiles` during its next cleanup cycle (typically after new transcriptions are saved), removing oldest recordings first until the total byte count falls below `audioHistoryBudgetBytes`.

### What format does the ZIP export use?

The ZIP contains two items: a `manifest.jsonl` file (newline-delimited JSON) where each row is an `AudioManifestRow` describing one recording, and an `audio/` folder containing the actual WAV files named with ISO-8601 timestamps and UUIDs (e.g., `2024-07-01T12-00-00Z_12345678.wav`). This structure allows external tools to parse metadata without parsing filenames.

### Can I export only specific date ranges of audio history?

Yes. The `exportAudioArchive(entries:to:)` method accepts any array of `TranscriptionHistoryEntry` objects. You can filter `TranscriptionHistoryStore.shared.allEntries` by date, transcription model, or any other property before passing the subset to the export function, creating archives containing only the desired recordings.