Skip to main content

A pure Python library for parsing and managing RIFF format files

Project description

riffy

PyPI version Python versions CI Docs License: MIT

Riffy provides a pure Python implementation for working with RIFF format files, with initial support for WAV audio files. The library is designed to have zero external dependencies while providing robust parsing capabilities for reading, modifying, and writing RIFF chunks.

Features

  • Zero Dependencies: Pure Python implementation with no external dependencies
  • WAV File Support: Complete WAV file parsing with format validation
  • RIFF Chunk Management: Access, add, replace, copy, and remove RIFF chunks
  • Chunk Modification & Writing: Modify chunks in memory and write valid WAV files back to disk
  • Audio Metadata Extraction: Extract sample rate, channels, bit depth, and duration
  • Format Validation: Automatic validation of file format and integrity
  • Type Safety: Full type hints (PEP 561 py.typed) for better IDE support and static analysis
  • Lightweight: Minimal footprint, perfect for embedded systems or restricted environments

Installation

From PyPI

pip install riffy

From Source

# Clone the repository
git clone https://github.com/jmcmeen/riffy.git
cd riffy

# Install in development mode
pip install -e .

# Or build and install
pip install build
python -m build
pip install dist/riffy-*.whl

For Development

# Install with development dependencies
pip install -e ".[dev]"

Quick Start

Basic WAV File Parsing

from riffy import WAVParser

# Parse a WAV file (parsing happens automatically on initialization)
parser = WAVParser("audio.wav")

# Access format information
info = parser.get_info()
print(f"Sample Rate: {info['format']['sample_rate']} Hz")
print(f"Channels: {info['format']['channels']}")
print(f"Bit Depth: {info['format']['bits_per_sample']} bits")
print(f"Duration: {info['duration_seconds']:.2f} seconds")

Accessing RIFF Chunks

from riffy import WAVParser

# Parsing happens automatically on initialization
parser = WAVParser("audio.wav")

# Access all chunks
for chunk_id, chunk in parser.chunks.items():
    print(f"Chunk: {chunk_id}, Size: {chunk.size} bytes, Offset: {chunk.offset}")

# Access specific chunk
if 'fmt ' in parser.chunks:
    fmt_chunk = parser.chunks['fmt ']
    print(f"Format chunk size: {fmt_chunk.size}")

Working with Audio Data

from riffy import WAVParser

# Parsing happens automatically on initialization
parser = WAVParser("audio.wav")

# Access raw audio data
audio_data = parser.audio_data
print(f"Audio data size: {len(audio_data)} bytes")

# Get sample count
info = parser.get_info()
print(f"Total samples: {info['sample_count']}")

Exporting Chunks to Binary Files

from riffy import WAVParser

# Parsing happens automatically on initialization
parser = WAVParser("audio.wav")

# Export raw audio data (most common use case)
bytes_written = parser.export_audio_data("raw_audio.bin")
print(f"Exported {bytes_written} bytes of audio data")

# Export specific chunks
parser.export_chunk('fmt ', "format_chunk.bin")
parser.export_chunk('data', "data_chunk.bin")

# List all available chunks before exporting
chunks = parser.list_chunks()
for chunk_id, info in chunks.items():
    print(f"Chunk '{chunk_id}': {info['size']} bytes at offset {info['offset']}")

Modifying and Writing WAV Files

Riffy can modify chunks in memory and write a valid WAV file back to disk:

from riffy import WAVParser

parser = WAVParser("audio.wav")

# Replace the audio data with new bytes
parser.replace_chunk('data', new_audio_bytes)

# Add a custom metadata chunk (IDs must be exactly 4 ASCII characters)
parser.add_chunk('INFO', b'Artist: Example\x00')

# set_chunk adds the chunk if missing, or replaces it if present
parser.set_chunk('NOTE', b'recorded 2026\x00')

# Copy a chunk from another file
other = WAVParser("other.wav")
parser.copy_chunk_from_parser('data', other)

# Write the modified file (overwrite=False refuses to clobber the source)
bytes_written = parser.write_wav("modified.wav")
print(f"Wrote {bytes_written} bytes")

Note: write_wav always writes the fmt chunk first, then data, then any remaining chunks in sorted order, so the output is not guaranteed to be byte-for-byte identical to the input. See Limitations.

Getting Detailed File Information

from riffy import WAVParser
import json

# Parsing happens automatically on initialization
parser = WAVParser("audio.wav")
info = parser.get_info()

# Pretty print all information
print(json.dumps(info, indent=2))

Output example:

{
  "file_path": "/path/to/audio.wav",
  "file_size": 1234567,
  "format": {
    "audio_format": 1,
    "channels": 2,
    "sample_rate": 44100,
    "byte_rate": 176400,
    "block_align": 4,
    "bits_per_sample": 16,
    "is_pcm": true
  },
  "duration_seconds": 3.5,
  "audio_data_size": 617400,
  "sample_count": 154350,
  "chunks": {
    "fmt ": 16,
    "data": 617400
  }
}

Supported Formats

Currently, Riffy supports:

  • WAV Files: PCM (uncompressed) audio only
  • RIFF Chunks: Standard chunk parsing, modification, and writing for WAV files

Planned Support

  • AVI files
  • WebP images
  • Additional WAV compression formats

Limitations

  • PCM only: Non-PCM (compressed) WAV files are rejected with UnsupportedFormatError.
  • Unique chunk IDs: chunks is keyed by chunk ID, so files containing multiple chunks with the same ID (e.g. several LIST chunks) keep only the last one.
  • Chunk ordering on write: write_wav emits fmt then data then remaining chunks sorted by ID, so a parse/write round-trip may not be byte-for-byte identical.

Requirements

  • Python 3.10 or higher
  • No external dependencies!

Development

# Install with development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=riffy --cov-report=html

# Lint and format with Ruff
ruff check .
ruff format .

# Type-check with mypy
mypy

See CONTRIBUTING.md for the full contributor guide.

Building the Docs

pip install -e ".[docs]"
mkdocs serve   # preview locally at http://127.0.0.1:8000

Contributing

Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.

License

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

Changelog

See CHANGELOG.md for the full release history.

References

Support

For bugs, feature requests, or questions, please open an issue.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

riffy-0.2.1.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

riffy-0.2.1-py3-none-any.whl (11.5 kB view details)

Uploaded Python 3

File details

Details for the file riffy-0.2.1.tar.gz.

File metadata

  • Download URL: riffy-0.2.1.tar.gz
  • Upload date:
  • Size: 13.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for riffy-0.2.1.tar.gz
Algorithm Hash digest
SHA256 06fc02079c2f74d6c573ca8ff3a64f4b61270819eba7360778c6a037c6f395c0
MD5 adbc0b3df2b8d544d415968d1a53d33c
BLAKE2b-256 193fdbab9f77c7b8ea29b8f7fc80c90d510d05d2256af3344c9e86389c0ba533

See more details on using hashes here.

Provenance

The following attestation bundles were made for riffy-0.2.1.tar.gz:

Publisher: publish.yml on jmcmeen/riffy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file riffy-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: riffy-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 11.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for riffy-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4a7433e49683022b6a903291308dd7e4c9bdf4d1d42ab1ae653cb7962e9cd3a2
MD5 65cb6dabee21485648bddbe79176ab1e
BLAKE2b-256 f50dce7d53677b89733b15eb3c787c51e36490a394bc53752c625d4781781754

See more details on using hashes here.

Provenance

The following attestation bundles were made for riffy-0.2.1-py3-none-any.whl:

Publisher: publish.yml on jmcmeen/riffy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page