Skip to content

About

Android app that analyzes FLAC files to verify whether they’re truly lossless. Detects lossy transcodes, fake hi-res, padded bit depth, corruption, and clipping through spectral analysis, integrity checks, and detailed audio measurements.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

SpectroFlac

SpectroFlac Home

Is this FLAC really lossless?

An offline Android analyzer that looks beyond the file extension.
Decode it. Analyze it. Verify it.

Release Stars Get it on Obtainium Android Kotlin Jetpack Compose

AGSL Room FFT Offline No network access


📥 Install

SpectroFlac is not on Google Play. Pick one of these:

Obtainium (recommended: updates itself from GitHub releases)

  1. Install Obtainium on your phone.
  2. In Obtainium tap Add App and paste:
    https://github.com/LinuxKernel44/SpectroFlac
    
  3. Tap Add, then Install. Obtainium will tell you when a new release is out.

Manual APK

  1. Download SpectroFlac-<version>.apk from the latest release.
  2. Open it and allow Install unknown apps for your browser or file manager when Android asks.

Releases are signed with the same key every time, so an update installed over either route keeps your history. If you still have v1.0.0, uninstall it first: it used an older key and Android refuses to update across keys.


What is SpectroFlac?

SpectroFlac is an Android application designed to answer a deceptively simple question:

Is this FLAC actually lossless?

A .flac extension alone doesn't tell you where the audio came from.

A file can be:

  • a genuine lossless master;
  • an MP3/AAC/Vorbis transcode wrapped in FLAC;
  • a fake 96/192 kHz file containing only 44.1/48 kHz content;
  • a 24-bit container containing only 16-bit samples;
  • corrupted;
  • or even an MP3 renamed to .flac.

SpectroFlac decodes the audio on-device, analyzes the complete spectrum and reports the evidence behind its verdict.

GENUINE  96%

24 BIT  44.1KHZ  1616 KBPS FLAC

Instead of simply saying "lossless" or "lossy", SpectroFlac provides the measurements used to reach its conclusion.


✨ Features

  • 🔬 Full-spectrum analysis
  • 🎵 Lossy transcode detection
  • 📈 Fake hi-res detection
  • 🧬 Bit-depth analysis
  • 🔐 FLAC MD5 verification
  • 🧮 Frame CRC-16 verification
  • 📊 RMS / peak / crest factor
  • 📉 Clipping detection
  • 🎚️ TT-style DR estimation
  • 🧾 Encoder fingerprints (MP3 CBR/VBR, AAC, Vorbis, Opus)
  • 🔎 Interactive spectrogram — pinch-zoom, pan, frequency/time cursor
  • 🎛️ Per-channel spectrum (left, right, mid, side)
  • 🎧 Stereo analysis — correlation, balance, width, phase problems
  • 🧩 Joint-stereo artifact detection
  • 🏷️ Metadata and tags
  • 🖼️ Embedded cover art
  • 📁 Recursive folder scanning
  • 🧵 Parallel analysis — several files at once (Auto, or 1–8), with heat and battery protection
  • 📋 Scan queue — per-file and overall progress, time left, pause / cancel / retry / reorder
  • 🔔 Background scans with a progress notification
  • 🎚️ Thread presets — Quiet, Balanced, Fast in one tap
  • ✅ Finish notification — "118 genuine, 4 not genuine, 1 damaged" when a scan ends
  • 🖼️ Spectrogram export — a PNG at any resolution, up to hundreds of megapixels, re-rendered from the file
  • ⚙️ Settings — performance, notifications, queue, history, auto-export, appearance
  • 📤 CSV / JSON export
  • 🕘 Local analysis history
  • 📱 Android share-sheet integration
  • 🪟 Liquid glass UI
  • 🔒 100% offline
  • 🚫 No network access
  • 🚫 No dangerous permissions — only a notification for background scans

📱 Screenshots

Home Transcode detected
Genuine file History

🧪 What it detects

Check Method
Lossy transcode Peak-hold FFT across the entire track and spectral cutoff analysis
Encoder profile Measured cutoff compared against known lossy encoder characteristics
Fake hi-res Detects content limited to the Nyquist range of lower sample rates
Padded bit depth Detects 24-bit containers whose lower 8 bits are consistently zero
Invalid FLAC Container sniffing identifies files based on their actual contents
Corruption STREAMINFO MD5 and FLAC frame CRC-16 verification
Loudness RMS and sample peak measurements
Dynamics Crest factor and TT-style DR estimation
Clipping Sample-level clipping and consecutive clipped runs

Example

A file claiming to be:

24-bit / 96 kHz

might actually contain:

16-bit / 44.1 kHz

SpectroFlac can identify evidence of both conditions rather than trusting the metadata.


🧠 How the analysis works

flowchart TD
    A[FLAC / Audio File] --> B[Container Sniffing]

    B -->|Not FLAC| X[Identify Actual Format]
    B -->|FLAC| C[Custom FLAC Decoder]

    C --> D[PCM Samples]

    D --> E[FFT / Spectrum]
    D --> F[Bit Depth Analysis]
    D --> G[Sample Rate Analysis]
    D --> H[Loudness & Dynamics]

    C --> I[STREAMINFO MD5]
    C --> J[Frame CRC-16]

    E --> K[Lossy Transcode Detection]
    E --> L[Fake Hi-Res Detection]

    F --> M[Padded Bit Depth Detection]
    G --> L

    E --> N[Encoder Fingerprint]

    I --> O[Integrity Result]
    J --> O

    K --> P[Analysis]
    L --> P
    M --> P
    N --> P
    O --> P
    H --> P

    P --> Q[Confidence]
    Q --> R[Final Verdict]
Loading

The important distinction is that SpectroFlac does not rely solely on metadata.

The audio itself is decoded and measured.


🔬 Spectral analysis

Lossy encoders commonly introduce a characteristic high-frequency cutoff.

SpectroFlac performs a peak-hold FFT over the complete track and examines the resulting spectrum.

For example, approximate encoder profiles can look like:

128 kbps  ────────────────╮
                          ╰──── cutoff

192 kbps  ───────────────────────╮
                                 ╰── cutoff

320 kbps  ───────────────────────────────╮
                                        ╰─ cutoff

The measured cutoff is then compared with known encoder characteristics.

This can provide strong evidence that a supposedly lossless file originated from a lossy source.

The fingerprint table was calibrated by round-tripping full-band stereo noise through libmp3lame (CBR and VBR), FFmpeg's AAC, libvorbis and libopus at 44.1 and 48 kHz and measuring where each cut landed (scripts/make-samples.sh regenerates that corpus). Many encoders share the same cutoff, so a cut suggests candidates; it never names one.

Interactive spectrogram

Tap the spectrogram strip on a result to open the full view:

  • pinch to zoom — fingers spread horizontally zoom time, vertically zoom frequency, diagonally both;
  • drag to pan, double-tap to zoom in on a spot or back out;
  • tap to place a crosshair that reads out the exact frequency, time and level; the Cursor toggle turns one-finger drags into cursor moves;
  • switch between Mid, Left, Right and Side (stereo files);
  • the Cutoff toggle draws the measured cutoff across the picture.

The result screen also shows a spectrum chart (peak-hold and average, per channel, with the cutoff marked) and a stereo card with a left/right correlation timeline.

Stereo and joint-stereo analysis

For stereo files SpectroFlac measures the left/right correlation, the balance, the side-to-mid width, and the share of the track that is out of phase; it also flags a silent channel and near-mono files.

Joint-stereo coders (MP3 joint stereo, AAC M/S, Vorbis coupling) spend their bits on the mid channel and starve the side channel. SpectroFlac looks for a side channel whose spectrum stops well below the mid channel's, and for a stereo image that narrows sharply from the mid range to the treble. This is reported as supporting evidence and never changes the verdict on its own.


⚠️ Important limitations

SpectroFlac is an analysis tool, not a magic provenance detector.

A genuinely dull master and a lossy transcode can sometimes produce similar spectra, especially when the cutoff is gradual.

Likewise, a lossy source can sometimes be made harder to identify through:

  • upsampling;
  • filtering;
  • resampling;
  • additional processing;
  • multiple generations of transcoding.

For this reason, SpectroFlac reports a confidence value and the measurements behind it instead of pretending that spectral analysis can prove the origin of every file.

Two limits worth knowing:

  • Very high bitrates leave no cut. AAC 256 kbps and up, LAME V0 and Vorbis q6 and up show no brick wall on a full-band signal, so the spectral test cannot see them. A clean spectrum proves nothing about those.
  • The thresholds are calibrated on a small corpus: synthetic noise through FFmpeg's encoders, one real lossless album, and lossy transcodes of three of its tracks. On that corpus every genuine file came out genuine and every transcode was caught, but other encoders (Apple, FDK, Fraunhofer) and other kinds of music (classical, hi-res, old masters with steep filters) can land elsewhere.

The spectral test is strong evidence, not proof.


🪟 Liquid Glass UI

SpectroFlac uses a custom liquid glass interface.

On Android 13+, panels use AGSL runtime shaders to refract the animated background behind them.

The effect includes:

  • glass-like refraction;
  • animated backgrounds;
  • chromatic dispersion;
  • translucent panels;
  • dynamic lighting;
  • shader-based distortion.

On Android 10–12, the application falls back to a frosted-glass presentation while keeping the same interface and functionality.


🛠️ Tech Stack

Layer Technology
Language Kotlin
UI Jetpack Compose
Graphics AGSL Runtime Shaders
Database Room
Audio decoder Custom Kotlin FLAC decoder
Signal processing FFT / spectral analysis
Serialization JSON
Build system Gradle + Kotlin DSL
File access Storage Access Framework
Minimum Android Android 10 / API 29
Modern graphics Android 13 / API 33+

Built with

Kotlin Android Gradle

Kotlin · Jetpack Compose · AGSL · Room · FFT · Custom FLAC Decoder


🏗️ Architecture

app/src/main/java/com/spectroflac/
│
├── flac/
│   ├── BitInput
│   ├── Container sniffing
│   ├── FLAC bitstream parsing
│   └── Full FLAC decoder
│
├── analysis/
│   ├── FFT
│   ├── Streaming measurements (per-channel spectra, stereo sums)
│   ├── Spectral analysis and encoder fingerprints
│   ├── Stereo / joint-stereo analysis
│   ├── Dynamics analysis
│   └── Verdict engine
│
├── data/
│   ├── Room database
│   └── JSON serialization
│
├── export/
│   ├── Text reports
│   ├── CSV
│   ├── JSON
│   └── Share intents
│
└── ui/
    ├── Compose screens (incl. the interactive spectrogram)
    ├── Charts (spectrum, correlation)
    └── glass/
        └── AGSL liquid glass system

🎧 Why a custom FLAC decoder?

SpectroFlac intentionally does not delegate FLAC decoding to MediaCodec.

The platform decoder may return 16-bit PCM on some devices.

That would make important parts of the analysis unreliable.

For example:

24-bit FLAC
     │
     ▼
Platform decoder
     │
     ▼
16-bit PCM
     │
     └── ❌ Original bit depth is already lost

SpectroFlac instead performs its own decoding:

24-bit FLAC
     │
     ▼
Custom Kotlin decoder
     │
     ▼
Original PCM samples
     │
     ├──► Bit-depth analysis
     ├──► FFT
     ├──► Dynamics
     └──► MD5 verification

This allows the application to inspect the decoded samples without depending on device-specific audio decoder behavior.


📂 Using SpectroFlac

Analyze a file

Select any file through the Android file picker.

SpectroFlac will determine whether the file is actually a FLAC stream.

An MP3 renamed to:

song.flac

will not be treated as a valid FLAC.


Scan a folder, or several files

Select a directory and SpectroFlac recursively analyzes its .flac files; or pick several files at once, or share several files to the app from a file manager. A single file still opens straight on its result; two or more go through the scan queue.

Files are queued while the folder is still being listed, so analysis starts immediately. More files or folders can be added while a scan runs.

The scan queue

  • an overall bar with 37 / 120 files, a percentage and the time left (computed from the bytes processed over the last seconds, so it already reflects how many files run in parallel; "Estimating…" until a few seconds of data exist);
  • one progress bar per running file, each with its own cancel button;
  • the waiting files, with a menu to analyse one next or remove it;
  • the finished files, filterable (genuine, suspicious, not genuine, damaged, errors) and sortable, with CSV / JSON export;
  • Pause / Resume, Cancel all, Retry failed, Clear finished.

A finished file keeps only its verdict and numbers, so a scan of thousands of files stays light on memory; tapping one re-analyzes it in a couple of seconds to rebuild the spectrogram and charts.

Multi-threading

A FLAC file decodes sequentially, so parallelism means several files analysed at the same time; one file always stays on one thread. Auto runs half of the phone's cores (4 on an 8-core phone), fewer when the phone gets hot or Battery Saver is on; a fixed number from 1 to 8 can be chosen instead. On a 4-core emulator the same 12 files took 7.9 s with 1 thread and 2.8 s with 4 (×2.8).

Background scans

While a scan runs, a foreground service with one progress notification keeps it going with the screen off (and holds a partial wake lock). It stops by itself when the queue is empty. It can be turned off in Settings.

Spectrogram export

Export spectrogram image (on the result screen and in the full spectrogram view) renders the spectrogram of the file to a PNG. Unlike a screenshot, it re-analyses the file at the resolution you ask for: the number of rows picks the FFT length (one bin per row or a little more) and the number of columns picks how far apart the analysis windows sit, so a bigger image really shows more detail, up to what the audio holds.

  • Size: presets (Screen 1920×1080, 4K, 8K), any exact width × height (256 px up to 32,768 rows and 1,000,000 columns), or Maximum — the finest analysis the file allows (16,384 rows ≈ 1.3 Hz each at 44.1 kHz, one column every ~256 samples), shrunk to fit your free storage. The dialog shows the detail per pixel (FFT size, Hz per row, ms per column), the final image size, the estimated PNG size and render time, and refuses sizes that do not fit.
  • Channel: Mid, Left, Right or Side for stereo files.
  • In the image: frequency and time axes with labels, a header (file, format, verdict, analysis details), the track title and artist with a small cover, the measured cutoff line, and a dBFS colour legend; each can be switched off.
  • Where it goes: Save as… (you pick the place and name) or Share.
  • How it can be so large: the audio is decoded once to a temporary file, the columns are computed on all cores into a second temporary file laid out so it can be read back row by row, and the PNG is streamed out while it is composed, so memory use stays small whatever the size (a 16,384 × 16,384 spectrogram — a 20,275 × 19,695 image — took 47 s on a 4-core emulator with the Java heap around 40 MB). Only free storage and time limit it.
  • It keeps running if you leave the app (foreground service with a progress notification and a Cancel button).

Settings

Section Options
Performance presets Quiet (¼ of the cores, low priority), Balanced (Auto) and Fast (all cores but one, full priority); files at once (Auto / 1–8), background scan, thermal protection, low-priority threads, keep the screen on, pause below a battery level
Notifications and battery notify when a scan finishes (on by default), the status of the notification permission and of the battery-optimization exemption, each with an Allow button
Queue skip files already analysed (unchanged file, same app version), offer to resume after a restart (default: start fresh)
Results and history history size, delete analyses older than N days, auto-export a CSV / JSON into a chosen folder when a scan ends
Appearance liquid glass effects, animated backdrop

Share to SpectroFlac

SpectroFlac registers itself for FLAC files.

You can therefore use:

Share → SpectroFlac

or:

Open with → SpectroFlac

directly from compatible file managers.


History

Every analysis can be stored locally using Room.

Previous reports can be:

  • reopened;
  • inspected;
  • exported;
  • compared manually.

No account or cloud service is required.


🔒 Privacy

SpectroFlac is designed to work entirely offline.

┌─────────────────────────────┐
│        Your device          │
│                             │
│  FLAC ──► Decode ──► FFT    │
│             │        │      │
│             ▼        ▼      │
│           MD5      Verdict  │
│                             │
└─────────────────────────────┘
             │
             X
        No network

SpectroFlac does not require:

  • ❌ Internet access
  • ❌ User accounts
  • ❌ Cloud processing
  • ❌ Audio uploads
  • ❌ Storage permissions

Files are accessed through Android's Storage Access Framework and analyzed locally.

For background scans and exports the app declares a foreground service (data sync), a wake lock, the notification permission (Android 13+) and the battery-optimization exemption request. The first time a scan starts, a short dialog explains why, then Android's own prompts follow: notifications, then "Let app always run in background?". If you refuse, nothing breaks (scans still run, without a visible notification, and Android may slow a long scan down with the screen off); the Settings screen shows the state of both and lets you grant them later. None of these give the app access to anything outside the files you pick.


🏭 Building

Clone the repository:

git clone https://github.com/LinuxKernel44/SpectroFlac.git
cd SpectroFlac

Build the debug APK:

./gradlew :app:assembleDebug

The APK will be generated at:

app/build/outputs/apk/debug/

Run the unit tests:

./gradlew :app:testDebugUnitTest

🔐 Signed release

Signing material is never committed to the repository.

Create a keystore:

keytool -genkeypair -v \
    -keystore spectroflac.jks \
    -alias spectroflac \
    -keyalg RSA \
    -keysize 4096 \
    -validity 10000

Create keystore.properties in the project root:

storeFile=/absolute/path/to/spectroflac.jks
storePassword=YOUR_PASSWORD
keyAlias=spectroflac
keyPassword=YOUR_PASSWORD

Both files are excluded through .gitignore.

Build the release:

./gradlew :app:assembleRelease

Without keystore.properties, the release build still works but produces an unsigned APK.


🚀 Publishing a release

The repository includes an automated release script:

scripts/release.sh

The script:

  1. Reads versionName from app/build.gradle.kts
  2. Builds the release APK
  3. Verifies the APK signature
  4. Creates or updates the corresponding GitHub release
  5. Attaches the signed APK

Before releasing, update:

versionCode
versionName

The resulting GitHub release follows:

vX.Y.Z

🧪 Testing

The FLAC decoder is tested against the MD5 checksum stored inside the FLAC STREAMINFO block.

This provides a strong end-to-end validation of the decoded audio.

Detection thresholds are also tested against samples with known provenance.

Set a sample directory:

SPECTROFLAC_SAMPLES=/path/to/samples \
./gradlew :app:testDebugUnitTest

The test suite expects:

real_*.flac  → genuine
fake_*.flac  → non-genuine
limit_*.flac → no expectation (encodes the spectral test cannot see)

scripts/make-samples.sh /path/to/samples builds such a corpus with FFmpeg (real stereo, MP3/AAC/Vorbis/Opus round-trips at several bitrates, upsampled and padded files) and checks each stage with ffprobe.

Synthetic tests (stereo statistics, the fingerprint table, per-channel layers) need no samples and always run.

The queue engine (scheduling, parallelism limit, pause, cancel, reorder, retry, thermal hold, skip-known, statistics, time-left estimator) has plain JVM tests with controllable fake analysers, so no device is needed.

The spectrogram export engine is unit-tested on the JVM too: the streaming PNG writer (validated by an independent decoder written from the spec, checking every chunk CRC), the strip-major level store that transposes the data, the STFT columns (a tone lights the right row; no splatter at the ends of the file), the planner (sizes, estimates, Maximum under a storage budget), axis ticks, layout and file names.

On a device or emulator there are instrumented tests:

  • the export dialog (presets, Maximum, free-space and size limits, channel and content choices);
  • the export itself on real files, decoded with Android's own BitmapFactory: exact size, header text, the red cutoff line, a lossy file's brick wall at the right row, each channel, a 33-megapixel image checked through BitmapRegionDecoder, progress phases, cancellation leaving no temporary file, the foreground service, a failed export leaving nothing behind (-e heavy true adds the 268-megapixel one);
  • the interactive spectrogram, driven with real touch gestures — horizontal, vertical and diagonal pinch, pan, double-tap, cursor;
  • the queue and settings screens (what is shown, what each control reports);
  • an end-to-end run of the real queue, analyser, settings and database over real FLAC files: parallelism limits, per-file progress, pause/resume, cancel, skip-known, queue saving, and a timing check that parallel is really faster (scripts/e2e-queue.sh prepares the files).
./gradlew :app:connectedDebugAndroidTest

🧪 Generating test samples

Using FFmpeg:

ffmpeg \
    -f lavfi \
    -i "anoisesrc=r=44100:duration=20:color=pink" \
    -ar 44100 \
    -sample_fmt s16 \
    -c:a flac \
    real_16_44.flac

Create a lossy intermediate:

ffmpeg \
    -i real_16_44.flac \
    -c:a libmp3lame \
    -b:a 128k \
    t.mp3

Convert it back to FLAC:

ffmpeg \
    -i t.mp3 \
    -ar 44100 \
    -sample_fmt s16 \
    -c:a flac \
    fake_mp3_128.flac

This produces a FLAC container whose audio originated from a 128 kbps MP3.


📊 Example verdict

┌──────────────────────────────────────┐
│                                      │
│             GENUINE                  │
│               96%                    │
│                                      │
│       24 BIT · 44.1 KHZ              │
│             1616 KBPS                │
│                                      │
├──────────────────────────────────────┤
│                                      │
│  Spectrum       ✓ No obvious cutoff  │
│  Bit depth     ✓ Genuine 24-bit     │
│  Sample rate   ✓ 44.1 kHz content   │
│  STREAMINFO    ✓ MD5 valid           │
│  Frames        ✓ CRC valid           │
│  Clipping      ✓ None detected       │
│                                      │
└──────────────────────────────────────┘

The application exposes the measurements behind the verdict rather than hiding them behind a single boolean.


🗺️ Roadmap

Done in 1.3.0

  • Spectrogram export to PNG at any resolution (re-rendered from the file, streamed to disk)
  • Thread presets: Quiet, Balanced, Fast
  • Notify when a scan finishes (on by default)
  • Notification permission and battery-optimization exemption, asked on the first scan and manageable in Settings

Done in 1.2.0

  • Parallel analysis of several files
  • Scan queue with per-file and overall progress, time left, pause / cancel / retry / reorder
  • Background scans with a progress notification
  • Settings (performance, queue, history, auto-export, appearance)
  • Pick several files, share several files, add files while a scan runs

Done in 1.1.0

  • Full interactive spectrogram (pinch, pan, cursor), per-channel spectrum, stereo and joint-stereo analysis, more encoder fingerprints

Ideas

  • Calibrate the fingerprints and thresholds on more real music (one album so far)
  • Fingerprints for Apple AAC, FDK-AAC and Fraunhofer MP3
  • Multichannel (5.1 / 7.1) stereo-style analysis
  • Compare two files (original vs. suspect)
  • Watch a folder and scan new files automatically

📜 Disclaimer

SpectroFlac cannot determine the complete historical provenance of an audio file with certainty.

Its verdict is based on measurable characteristics of the file and decoded audio.

A file that has been heavily processed, filtered, resampled, or transcoded multiple times may not retain obvious evidence of its source.

Use the confidence score and underlying measurements as evidence, not absolute proof.


👨‍💻 Author

LinuxKernel44

GitHub: https://github.com/LinuxKernel44


📄 License

Licensed under the PolyForm Noncommercial License 1.0.0.

You may use, modify, and share SpectroFlac for any noncommercial purpose, provided that every copy keeps the license terms and the Required Notice: credit line from LICENSE. Commercial use requires a separate agreement with the author.


SpectroFlac
Decode it. Analyze it. Verify it.

About

Android app that analyzes FLAC files to verify whether they’re truly lossless. Detects lossy transcodes, fake hi-res, padded bit depth, corruption, and clipping through spectral analysis, integrity checks, and detailed audio measurements.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages