Skip to content

cannellegrdt/Stone-Analysis

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stone Analysis

Audio signal analyzer and steganography tool using Discrete Fourier Transform - EPITECH G-CNA-400 Project


Table of contents

  1. Overview
  2. Compilation
  3. Usage a) Analyze mode b) Cypher mode c) Decypher mode
  4. WAV file format
  5. Signal processing a) Discrete Fourier Transform b) Cooley-Tukey FFT
  6. Steganography a) Character set b) Encoding algorithm
  7. Project architecture a) File tree b) Class diagram

Overview

Stone Analysis reads an audio signal from a .wav file and can:

  • Analyze the signal — decompose it via FFT and display the N dominant frequencies.
  • Cypher a message — hide a secret text inside an audio file without audible difference.
  • Decypher a message — extract and reconstruct the hidden text from a cyphered file.

Audio files must be encoded in 16-bit PCM mono at 48 kHz. No external audio or DSP library is used; the FFT and steganography are fully hand-implemented.

Subject is in G-CNA-400_stone_analysis.pdf file.


Compilation

# Compile the main project
make

# Clean object files
make clean

# Remove everything (objects + binary)
make fclean

# Recompile from scratch
make re

# Compile and run unit tests (Criterion)
make unit_tests

# Coverage report on unit tests
make coverage

# Memory check (Valgrind)
make memcheck

Usage

./stone_analysis [--analyze | -a] IN_FILE N
                 [--cypher  | -c] IN_FILE OUT_FILE MESSAGE
                 [--decypher| -d] IN_FILE
Argument Description
IN_FILE Input .wav audio file
OUT_FILE Output .wav file (cypher mode only)
MESSAGE Text to hide in the audio file
N Number of top frequencies to display

Errors are printed to stderr and the program exits with code 84.


Analyze mode (--analyze / -a)

Decomposes the audio signal via FFT and prints the N dominant frequencies in decreasing order of magnitude.

./stone_analysis --analyze input.wav 3
Top 3 frequencies:
494.0 Hz
330.0 Hz
415.0 Hz

Cypher mode (--cypher / -c)

Hides a secret message inside the audio file. The output file is identical in size to the input and has the exact same WAV header.

./stone_analysis --cypher input.wav output.wav "Miaou"
$ du -b input.wav output.wav
368492    input.wav
368492    output.wav
  • Accepts letters (a–z, A–Z), digits (0–9), and spaces.
  • The message is case-insensitive at encoding; it is decoded in uppercase.

Decypher mode (--decypher / -d)

Extracts and prints the message hidden in a previously cyphered file.

./stone_analysis --decypher output.wav
MIAOU

WAV file format

The program only accepts 16-bit PCM mono WAV at 48 kHz. Files that do not match this spec are rejected with an error.

Field Expected value
chunkId RIFF
format WAVE
audioFormat 1 (PCM)
numChannels 1 (mono)
sampleRate 48000 Hz
bitsPerSample 16

The header is read as a raw 44-byte struct. For cypher mode, the output header is copied byte-for-byte from the input.


Signal processing

Discrete Fourier Transform

The DFT decomposes a discrete time-domain signal $x[n]$ of length $N$ into its frequency components:

$$X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j \frac{2\pi}{N} k n}, \quad k = 0, 1, \ldots, N-1$$

The magnitude at bin $k$ is $|X[k]|$, and the corresponding frequency is:

$$f_k = k \cdot \frac{f_s}{N}$$

where $f_s = 48000$ Hz is the sampling rate.

Cooley-Tukey FFT optimization

A naïve DFT has complexity $O(N^2)$, which would take tens of seconds on a typical 3-second audio file. This implementation uses the iterative Cooley-Tukey FFT algorithm, reducing complexity to $O(N \log N)$.

The input is zero-padded to the next power of 2, then processed in-place using the butterfly operation:

for each stage (length = 2, 4, 8, … N):
    for each block of size `length`:
        w = twiddle factor e^(-j·2π/length)
        butterfly(a[i+j], a[i+j+length/2], w^j)

This brings analysis of a 184 000-sample file from ~30 s down to < 0.3 s.

For the IDFT, the same algorithm is applied with the sign of the exponent inverted and a final division by $N$.


Steganography

Character set

Characters Indices
az / AZ 0–25
09 26–35
(space) 36

Each character is encoded on 6 bits (2^6 = 64 > 37 values).

Encoding algorithm

The algorithm uses spread-spectrum LSB steganography:

  1. The message length is encoded first as a 16-bit integer.
  2. Each character is then encoded as a 6-bit index.
  3. Each bit is stored in the LSB of the audio sample at position:

$$\text{pos}(i) = (\text{SEED} + i \times \text{PRIME}) \bmod N$$

with $\text{SEED} = 42$ and $\text{PRIME} = 7919$.

Using a large prime as the stride distributes the bits evenly across the entire file (spread-spectrum technique), making the hidden data statistically indistinguishable from natural signal noise. The modification is a ±1 change on a 16-bit sample, which is imperceptible to the ear ($\approx 0.003$% of full scale).

The output file is always exactly the same size as the input.


Project architecture

File tree

.
├── include/
│   ├── Core.hpp              # Argument parsing, mode dispatch
│   ├── Error.hpp             # Custom exception class
│   ├── SignalProcessor.hpp   # FFT / IFFT / frequency analysis
│   ├── Steganographer.hpp    # Spread-spectrum LSB encode/decode
│   └── WavHandler.hpp        # WAV file I/O and header validation
├── src/
│   ├── main.cpp              # Entry point
│   ├── Core.cpp
│   ├── Error.cpp
│   ├── SignalProcessor.cpp
│   ├── Steganographer.cpp
│   └── WavHandler.cpp
├── tests/
│   └── unit_tests/           # Criterion unit tests
├── G-CNA-400_stone_analysis.pdf
└── Makefile

Class diagram

classDiagram
    direction TB

    class Core {
        -_config : Config
        +Core(argc, argv)
        +run() int
        -parseArgs(argc, argv) void
        -displayHelp() void
        -executeAnalyze() void
        -executeCypher() void
        -executeDecypher() void
    }

    class Config {
        +mode : Mode
        +inFile : string
        +outFile : string
        +message : string
        +nTopFreqs : size_t
    }

    class Mode {
        <<enum>>
        HELP
        ANALYZE
        CYPHER
        DECYPHER
    }

    class Error {
        -_message : string
        +Error(message)
        +what() const char*
    }

    class WavHandler {
        -_header : WavHeader
        -_samples : vector~int16_t~
        +load(filename) void
        +save(filename) void
        +getSamples() vector~int16_t~
        +setSamples(samples) void
        +getHeader() WavHeader
        -validateHeader() void
    }

    class WavHeader {
        +chunkId[4] : char
        +sampleRate : uint32_t
        +bitsPerSample : uint16_t
        +numChannels : uint16_t
        +...
    }

    class SignalProcessor {
        +computeDFT(samples)$ ComplexVector
        +computeIDFT(freqs)$ vector~int16_t~
        +getTopFrequencies(freqs, n, sr)$ vector~FrequencyMagnitude~
    }

    class Steganographer {
        +cypher(samples, message)$ void
        +decypher(samples)$ string
        -charToIndex(c)$ uint8_t
        -indexToChar(idx)$ char
    }

    Core --> Config : owns
    Core --> WavHandler : uses
    Core --> SignalProcessor : uses
    Core --> Steganographer : uses
    Core ..> Error : throws
    WavHandler ..> Error : throws
    WavHandler --> WavHeader : owns
    Config --> Mode : uses
Loading

About

Audio signal analyzer and steganography tool using Discrete Fourier Transform.

Topics

Resources

Stars

Watchers

Forks

Contributors