diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..1523fbc --- /dev/null +++ b/.coveragerc @@ -0,0 +1,28 @@ +[run] +source = pyflac +relative_files = True +concurrency = thread + +omit = + */tests/* + .tox/* + setup.py + */__init__.py + */__main__.py + */examples/* + */libraries/* + */builder/* + +[report] +omit = + */tests/* + .tox/* + setup.py + */__init__.py + */__main__.py + */examples/* + */libraries/* + */builder/* +exclude_lines = + pragma: no cover + raise NotImplementedError diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..92feca6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,146 @@ +name: build + +on: + workflow_dispatch: + inputs: + version: + description: 'The version of libFLAC to build' + required: false + default: '1.3.3' + debug: + description: 'Enable debug' + required: false + default: 'no' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Install dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y \ + autoconf \ + clang \ + gcc-arm-linux-gnueabihf \ + g++-arm-linux-gnueabihf \ + libtool \ + mingw-w64 \ + patchelf \ + wget + - name: Download libFLAC + run: | + wget https://ftp.osuosl.org/pub/xiph/releases/flac/flac-${{ github.event.inputs.version }}.tar.xz + + - name: Build libFLAC (Linux) + run: | + tar xJf flac-${{ github.event.inputs.version }}.tar.xz + mv flac-${{ github.event.inputs.version }} flac-linux-x86_64 + cd flac-linux-x86_64 + CC=clang ./configure --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} --enable-shared --disable-static --disable-examples + make + ls -l src/libFLAC/.libs + mkdir ../linux-x86_64 + cp src/libFLAC/.libs/libFLAC.so.8.3.0 ../linux-x86_64/libFLAC-8.3.0.so + cd ../linux-x86_64 + patchelf --set-soname libFLAC-8.3.0.so libFLAC-8.3.0.so + - name: Upload Linux library + uses: actions/upload-artifact@v1 + with: + name: linux-x86_64 + path: linux-x86_64 + + - name: Build libFLAC (Windows x64) + run: | + tar xJf flac-${{ github.event.inputs.version }}.tar.xz + mv flac-${{ github.event.inputs.version }} flac-windows-x86_64 + cd flac-windows-x86_64 + ./configure --host=x86_64-w64-mingw32 --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} --enable-shared --disable-static --disable-examples + make + ls -l src/libFLAC/.libs + mkdir ../windows-x86_64 + cp src/libFLAC/.libs/libFLAC-8.dll ../windows-x86_64/ + cp src/libFLAC/.libs/libFLAC.dll.a ../windows-x86_64/FLAC-8.lib + - name: Upload Windows x64 library + uses: actions/upload-artifact@v1 + with: + name: windows-x86_64 + path: windows-x86_64 + + - name: Build libFLAC (Windows x86) + run: | + tar xJf flac-${{ github.event.inputs.version }}.tar.xz + mv flac-${{ github.event.inputs.version }} flac-windows-i686 + cd flac-windows-i686 + ./configure --host=i686-w64-mingw32 --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} --enable-shared --disable-static --disable-examples + make + ls -l src/libFLAC/.libs + mkdir ../windows-i686 + cp src/libFLAC/.libs/libFLAC-8.dll ../windows-i686/ + cp src/libFLAC/.libs/libFLAC.dll.a ../windows-i686/FLAC-8.lib + - name: Upload Windows x64 library + uses: actions/upload-artifact@v1 + with: + name: windows-i686 + path: windows-i686 + + - name: Build libFLAC (Raspbian armv7a) + run: | + tar xJf flac-${{ github.event.inputs.version }}.tar.xz + mv flac-${{ github.event.inputs.version }} flac-raspbian-armv7a + cd flac-raspbian-armv7a + ./configure --host=arm-linux-gnueabihf --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} CFLAGS="-mfpu=neon -march=armv7-a -mfloat-abi=hard" --enable-shared --disable-static --disable-examples + make + ls -l src/libFLAC/.libs + mkdir ../raspbian-armv7a + cp src/libFLAC/.libs/libFLAC.so.8.3.0 ../raspbian-armv7a/libFLAC-8.3.0.so + cd ../raspbian-armv7a + patchelf --set-soname libFLAC-8.3.0.so libFLAC-8.3.0.so + - name: Upload Raspbian armv7a library + uses: actions/upload-artifact@v1 + with: + name: raspbian-armv7a + path: raspbian-armv7a + + - name: Build libFLAC (Raspbian armv6z) + run: | + tar xJf flac-${{ github.event.inputs.version }}.tar.xz + mv flac-${{ github.event.inputs.version }} flac-raspbian-armv6z + cd flac-raspbian-armv6z + ./configure --host=arm-linux-gnueabihf --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} CFLAGS="-mfpu=vfp -mfloat-abi=hard" --enable-shared --disable-static --disable-examples + make + ls -l src/libFLAC/.libs + mkdir ../raspbian-armv6z + cp src/libFLAC/.libs/libFLAC.so.8.3.0 ../raspbian-armv6z/libFLAC-8.3.0.so + cd ../raspbian-armv6z + patchelf --set-soname libFLAC-8.3.0.so libFLAC-8.3.0.so + - name: Upload Raspbian armv6z library + uses: actions/upload-artifact@v1 + with: + name: raspbian-armv6z + path: raspbian-armv6z + + build_macos: + runs-on: macos-latest + steps: + - name: Install dependencies + run: brew install wget + - name: Download libFLAC + run: | + wget https://ftp.osuosl.org/pub/xiph/releases/flac/flac-${{ github.event.inputs.version }}.tar.xz + tar xvf flac-${{ github.event.inputs.version }}.tar.xz + - name: Build libFLAC + run: | + cd flac-${{ github.event.inputs.version }} + CC=clang ./configure --with-ogg=no --enable-debug=${{ github.event.inputs.debug }} --enable-shared --disable-static --disable-examples + make + mkdir ../darwin-x86_64 + ls -l src/libFLAC/.libs + cp src/libFLAC/.libs/libFLAC.8.dylib ../darwin-x86_64/ + cd ../darwin-x86_64 + install_name_tool -id @rpath/libFLAC.8.dylib libFLAC.8.dylib + - name: Upload macOS library + uses: actions/upload-artifact@v1 + with: + name: darwin-x86_64 + path: darwin-x86_64 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..281edf8 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: lint + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +jobs: + + flake8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Lint the Python code + uses: TrueBrain/actions-flake8@master + with: + path: pyflac diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..42d0078 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,60 @@ +name: tests + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +jobs: + + linux: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ '3.6', '3.7', '3.8', '3.9' ] + + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update -y + sudo apt-get install libsndfile1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Run tests + run: tox + + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Run tests + run: tox + + windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Check install + run: python setup.py install diff --git a/.gitignore b/.gitignore index b6e4761..d8ac85b 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ htmlcov/ .cache nosetests.xml coverage.xml +junit-report.xml *.cover *.py,cover .hypothesis/ @@ -127,3 +128,19 @@ dmypy.json # Pyre type checker .pyre/ + +# VSCode +.vscode/ + +# macOS +.DS_Store + +# CFFI +*.o +*.c + +# Audio +*.flac + +# Development +.archive \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..bb13ca6 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,23 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: docs/requirements.txt + +submodules: + exclude: all diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..cfbbcbb --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,10 @@ +pyFLAC Changelog +---------------- + +**v1.0.0** + +* Added a `StreamEncoder` to compress raw audio data on-the-fly into a FLAC byte stream +* Added a `StreamDecoder` to decompress a FLAC byte stream back to raw audio data +* Added a `FileEncoder` to convert a WAV file to FLAC encoded data, optionally saving to a FLAC file +* Added a `FileDecoder` to convert a FLAC file to raw audio data, optionally saving to a WAV file +* Bundled with libFLAC version 1.3.3 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..f99b1db --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,49 @@ +Contributing +------------ + +If you find any bugs or other things that need improvement, +or would like to add additional features, please create an issue or a pull request at +https://github.com/sonos/pyFLAC +Contributions are always welcome! + +You get started, grab the latest version of the code from GitHub:: + + git clone https://github.com/sonos/pyFLAC.git + cd pyflac + +To build the package for development:: + + python3 pyflac/builder/encoder.py + + python3 pyflac/builder/decoder.py + +you can also install your local copy with pip:: + + pip3 install . + +Before submitting a pull request, make sure all tests are passing and the +test coverage has not been decreased. + +Testing +------- + +To run the test suite:: + + tox -r + +Documentation +------------- + +If you make changes to the documentation, you can locally re-create the HTML +pages using Sphinx_. +You can install it and the read the docs theme with:: + + pip3 install -r docs/requirements.txt + +To create the HTML pages, use:: + + python3 setup.py build_sphinx + +The generated files will be available in the directory ``docs/_build/html``. + +.. _Sphinx: http://sphinx-doc.org/ diff --git a/LICENSE b/LICENSE.txt similarity index 99% rename from LICENSE rename to LICENSE.txt index 261eeb9..e6c000c 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2021 Sonos, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..37f4ddb --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,21 @@ +include LICENSE.txt +include README.rst +include CHANGELOG.rst +include CONTRIBUTING.rst +include examples/*.py +include tests/__init__.py +include tests/data/*.wav +include tests/data/*.flac +include tests/test_encoder.py +include tests/test_decoder.py +include pyflac/builder/*.py +include pyflac/include/FLAC/*.h +include pyflac/libraries/LICENSE +include pyflac/libraries/darwin-x86_64/*.dylib +include pyflac/libraries/linux-x86_64/*.so +include pyflac/libraries/raspbian-armv7a/*.so +include pyflac/libraries/raspbian-armv6z/*.so +include pyflac/libraries/windows-i686/*.dll +include pyflac/libraries/windows-i686/*.lib +include pyflac/libraries/windows-x86_64/*.dll +include pyflac/libraries/windows-x86_64/*.lib diff --git a/README.md b/README.md deleted file mode 100644 index 95fdf68..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# pyFLAC -A Python wrapper around libFLAC diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..47b114d --- /dev/null +++ b/README.rst @@ -0,0 +1,54 @@ +pyFLAC +====== + +.. image:: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/lint.yml/badge.svg + :target: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/lint.yml +.. image:: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/test.yml/badge.svg + :target: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/test.yml +.. image:: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/build.yml/badge.svg + :target: https://github.com/Sonos-Inc/pyFLAC/actions/workflows/build.yml +.. image:: https://img.shields.io/pypi/pyversions/pysoundio + :target: https://pypi.org/project/pyFLAC + +A simple Pythonic interface for `libFLAC `_. + + FLAC stands for Free Lossless Audio Codec, an audio format similar to MP3, but lossless, + meaning that audio is compressed in FLAC without any loss in quality. This is similar to + how Zip works, except with FLAC you will get much better compression because it is designed + specifically for audio. + +pyFLAC allows you to encode and decode raw audio data directly to/from a file, or in real-time +using callbacks. + +Installation +------------ + +You can use pip to download and install the latest release with a single command. :: + + pip3 install pyflac + +.. note:: + pyFLAC depends on `libsndfile`, which requires an extra install step on Linux distributions. + See the `SoundFile `_ documentation for more information. + + +Supported platforms +------------------- + +- macOS +- Linux +- RPi Zero/2/3/4 +- Windows 7/8/10 + + +CLI +--- + +pyFLAC comes bundled with a command line tool to quickly convert between WAV and FLAC files. +For more information, print the help info. :: + + pyflac --help + +.. note:: + If you didn't install pyFLAC globally then the command line tool will not be installed on your PATH. + However you should still be able to access the tool with `python3 -m pyflac`. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..1f51ff4 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,92 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('..')) + + +# -- Project information ----------------------------------------------------- + +project = 'pyFLAC' +copyright = '2021, Sonos, Inc' +author = 'Joe Todd' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +needs_sphinx = '1.3' # for napoleon + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', +] + +napoleon_google_docstring = True +napoleon_numpy_docstring = False +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = False +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = False +napoleon_use_rtype = False + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + 'collapse_navigation': False +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..b7a7d8c --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,120 @@ +.. default-role:: py:obj + +.. include:: ../README.rst + +.. only:: html + +Examples +-------- + +:download:`passthrough.py <../examples/passthrough.py>` + +Read a WAV file and pass the audio through the encoder/decoder for the purposes of illustration. :: + + python3 passthrough.py + +This example asserts that the uncompressed data is exactly equal to the original signal. + +:download:`stream.py <../examples/stream.py>` + +Stream audio from the microphone input and pass through the encoder printing the effectiveness +of the compression to the terminal. :: + + python3 stream.py + +.. note:: + This example requires `sounddevice`, which can be installed with pip. + See the `sounddevice `_ documentation for more information. + + +Limitations +----------- + +- pyFLAC currently only supports 16-bit audio. +- FLAC metadata handling is not implemented. +- Seek/tell functionality is not implemented. + + +API Reference +============= + +Encoder +------- + +To encode raw audio data with pyFLAC you can either write encoded +data directly to a file or process in real-time. + +.. autoclass:: pyflac.FileEncoder + :members: + :undoc-members: + +.. autoclass:: pyflac.StreamEncoder + :members: + :undoc-members: + :inherited-members: + +Decoder +------- + +To decode compressed data with pyFLAC you can either read the compressed +data directly from a file or process in real-time. + +.. autoclass:: pyflac.FileDecoder + :members: + :undoc-members: + +.. autoclass:: pyflac.StreamDecoder + :members: + :undoc-members: + :inherited-members: + +State +----- + +.. autoclass:: pyflac.EncoderState + :members: + :undoc-members: + :inherited-members: + +.. autoclass:: pyflac.DecoderState + :members: + :undoc-members: + :inherited-members: + +Exceptions +---------- + +.. autoclass:: pyflac.EncoderInitException + +.. autoclass:: pyflac.EncoderProcessException + +.. autoclass:: pyflac.DecoderInitException + +.. autoclass:: pyflac.DecoderProcessException + + +Development +=========== + +.. only:: html + +.. include:: ../CONTRIBUTING.rst + +.. include:: ../CHANGELOG.rst + +License +======= + +pyFLAC is distributed under an Apache 2.0 license allowing users to use the software for any purpose, +to distribute it and to modify it. + +pyFLAC includes prebuilt binaries of libFLAC for different architectures, these binaries are distributed +under the following libFLAC license. + +.. include:: ../pyflac/libraries/LICENSE + :literal: + +Index +===== + +* :ref:`genindex` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..2543925 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx +sphinx-rtd-theme \ No newline at end of file diff --git a/examples/passthrough.py b/examples/passthrough.py new file mode 100755 index 0000000..851ef7e --- /dev/null +++ b/examples/passthrough.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC passthrough example +# +# This example reads a WAV file, passes it through the FLAC encoder, and +# then back through through the FLAC decoder. It also asserts that the +# uncompressed output is exactly equal to the original signal. +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import argparse +from pathlib import Path +import queue + +import numpy as np +import soundfile as sf +import pyflac + + +class Passthrough: + + def __init__(self, args): + self.idx = 0 + self.total_bytes = 0 + self.queue = queue.SimpleQueue() + self.data, self.sr = sf.read(args.input_file, dtype='int16', always_2d=True) + + self.encoder = pyflac.StreamEncoder( + callback=self.encoder_callback, + sample_rate=self.sr, + blocksize=args.block_size, + ) + + self.decoder = pyflac.StreamDecoder( + callback=self.decoder_callback + ) + + def encode(self): + self.encoder.process(self.data) + self.encoder.finish() + + def decode(self): + while not self.queue.empty(): + self.decoder.process(self.queue.get()) + self.decoder.finish() + + def encoder_callback(self, + buffer: bytes, + num_bytes: int, + num_samples: int, + current_frame: int): + self.total_bytes += num_bytes + self.queue.put(buffer) + + def decoder_callback(self, + data: np.ndarray, + sample_rate: int, + num_channels: int, + num_samples: int): + assert self.sr == sample_rate + assert np.array_equal(data, self.data[self.idx:self.idx + num_samples]) + self.idx += num_samples + + +def main(): + parser = argparse.ArgumentParser( + description='pyFLAC passthrough example', + epilog='Pass a WAV file through the FLAC encoder/decoder and verify the output signal' + ) + parser.add_argument('input_file', type=Path, help='Input WAV file to passthrough') + parser.add_argument('-c', '--compression-level', type=int, choices=range(0, 9), default=5, + help='0 is the fastest compression, 5 is the default, 8 is the highest compression') + parser.add_argument('-b', '--block-size', type=int, default=0, help='The block size') + args = parser.parse_args() + + flac = Passthrough(args) + flac.encode() + flac.decode() + + print('Verified OK') + print('Compression ratio = {ratio:.2f}%'.format( + ratio=flac.total_bytes / flac.data.nbytes * 100 + )) + + +if __name__ == '__main__': + main() diff --git a/examples/stream.py b/examples/stream.py new file mode 100755 index 0000000..526a289 --- /dev/null +++ b/examples/stream.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC audio stream example +# +# This example streams audio from the microphone input, passes it through +# the FLAC encoder and prints the effectiveness of the data compression. +# +# Requires sounddevice. +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import argparse +from pathlib import Path +import queue +import threading +import time + +import numpy as np +import pyflac +import sounddevice as sd + + +class ProcessingThread(threading.Thread): + + def __init__(self, args, stream): + super().__init__() + self.output_file = None + self.queue = queue.SimpleQueue() + self.num_channels = stream.channels + self.sample_size = stream.samplesize + + self.encoder = pyflac.StreamEncoder(callback=self.encoder_callback, + sample_rate=int(stream.samplerate), + compression_level=args.compression_level) + + if args.output_file: + self.output_file = open(args.output_file, 'wb') + + def start(self): + self.running = True + super().start() + + def stop(self): + self.running = False + + def encoder_callback(self, buffer, num_bytes, num_samples, current_frame): + if num_samples == 0: + print('FLAC header') + else: + print('{i}: Encoded {actual_bytes} bytes in {num_bytes} bytes: {ratio:.2f}%'.format( + i=current_frame, + actual_bytes=num_samples * self.num_channels * self.sample_size, + num_bytes=num_bytes, + ratio=num_bytes / (num_samples * self.num_channels * self.sample_size) * 100 + )) + + if self.output_file: + self.output_file.write(buffer) + self.output_file.flush() + + def run(self): + while self.running: + while not self.queue.empty(): + data = np.frombuffer(self.queue.get(), dtype=np.int16) + samples = data.reshape((len(data) // self.num_channels, self.num_channels)) + self.encoder.process(samples) + time.sleep(0.1) + + self.encoder.finish() + if self.output_file: + print(f'Wrote output to {self.output_file.name}') + self.output_file.close() + + +class AudioStream: + + def __init__(self, args): + self.stream = sd.RawInputStream( + dtype='int16', + blocksize=args.block_size, + callback=self.audio_callback + ) + self.thread = ProcessingThread(args, self.stream) + + def start(self): + self.thread.start() + self.stream.start() + + def stop(self): + self.stream.stop() + self.thread.stop() + + def audio_callback(self, indata, frames, sd_time, status): + self.thread.queue.put(bytes(indata)) + + +def main(): + parser = argparse.ArgumentParser( + description='pyFLAC audio stream example', + epilog='Stream audio data from the microphone and pass it through the pyFLAC encoder' + ) + parser.add_argument('-o', '--output-file', type=Path, help='Optionally save to output FLAC file') + parser.add_argument('-c', '--compression-level', type=int, choices=range(0, 9), default=5, + help='0 is the fastest compression, 5 is the default, 8 is the highest compression') + parser.add_argument('-b', '--block-size', type=int, default=0, help='The block size') + args = parser.parse_args() + + try: + stream = AudioStream(args) + stream.start() + while True: + time.sleep(0.1) + except KeyboardInterrupt: + stream.stop() + + +if __name__ == '__main__': + main() diff --git a/pyflac/__init__.py b/pyflac/__init__.py new file mode 100644 index 0000000..01190a1 --- /dev/null +++ b/pyflac/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +__title__ = 'pyFLAC' +__version__ = '1.0.0-beta.2' +__all__ = [ + 'StreamEncoder', + 'FileEncoder', + 'EncoderState', + 'EncoderInitException', + 'EncoderProcessException', + 'StreamDecoder', + 'FileDecoder', + 'DecoderState', + 'DecoderInitException', + 'DecoderProcessException' +] + +import os +import platform + +from cffi import FFI + +# ------------------------------------------------------------------------------ +# Fix DLL load for Windows +# +# Since there is no rpath equivalent for Windows, we just explicitly load +# the libFLAC DLL here. +# ------------------------------------------------------------------------------ +if platform.system() == 'Windows': + ffi = FFI() + base_path = os.path.dirname(os.path.abspath(__file__)) + if platform.architecture()[0] == '32bit': + libflac = ffi.dlopen(os.path.join(base_path, 'libraries', 'windows-i686', 'libFLAC-8.dll')) + elif platform.architecture()[0] == '64bit': + libflac = ffi.dlopen(os.path.join(base_path, 'libraries', 'windows-x86_64', 'libFLAC-8.dll')) + + +# flake8: noqa: F401 +from .encoder import ( + StreamEncoder, + FileEncoder, + EncoderState, + EncoderInitException, + EncoderProcessException +) +from .decoder import ( + StreamDecoder, + FileDecoder, + DecoderState, + DecoderInitException, + DecoderProcessException +) diff --git a/pyflac/__main__.py b/pyflac/__main__.py new file mode 100644 index 0000000..8487507 --- /dev/null +++ b/pyflac/__main__.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC file reader/writer +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import argparse +from pathlib import Path +import os + +from pyflac import FileEncoder, FileDecoder + + +def get_args(): + parser = argparse.ArgumentParser( + description='pyFLAC encoder/decoder', + epilog='Convert WAV files to FLAC and vice versa' + ) + parser.add_argument('input_file', type=Path, help='Input file to encode/decode') + parser.add_argument('-o', '--output-file', type=Path, help='Output file') + parser.add_argument('-c', '--compression-level', type=int, choices=range(0, 9), default=5, + help='0 is the fastest compression, 5 is the default, 8 is the highest compression') + parser.add_argument('-b', '--block-size', type=int, default=0, help='The block size') + parser.add_argument('-v', '--verify', action='store_false', default=True, help='Verify the compressed data') + args = parser.parse_args() + return args + + +def main(): + args = get_args() + with open(args.input_file, 'rb') as f: + header = f.read(4).decode().upper() + + filename, extension = os.path.splitext(args.input_file) + if header == 'RIFF': + args.output_file = f'{filename}.flac' if args.output_file is None else args.output_file + encoder = FileEncoder( + input_file=args.input_file, + output_file=args.output_file, + blocksize=args.block_size, + compression_level=args.compression_level, + verify=args.verify + ) + encoder.process() + elif header == 'FLAC': + args.output_file = f'{filename}.wav' if args.output_file is None else args.output_file + decoder = FileDecoder(args.input_file, args.output_file) + decoder.process() + else: + raise ValueError('Please provide either a WAV or a FLAC file') + + +if __name__ == '__main__': + main() diff --git a/pyflac/builder/build_args.py b/pyflac/builder/build_args.py new file mode 100644 index 0000000..5e137b4 --- /dev/null +++ b/pyflac/builder/build_args.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC builder +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import os +import platform +import subprocess + + +def get_build_kwargs(): + system = platform.system() + builder_path = os.path.dirname(os.path.realpath(__file__)) + package_path = os.path.abspath(os.path.join(builder_path, os.pardir)) + + build_kwargs = { + 'include_dirs': ['./pyflac/include'], + } + + if system == 'Darwin': + architecture = 'darwin-x86_64' + build_kwargs['libraries'] = ['FLAC.8'] + build_kwargs['library_dirs'] = [os.path.join(package_path, 'libraries', architecture)] + build_kwargs['extra_link_args'] = ['-Wl,-rpath,@loader_path/libraries/' + architecture] + + elif system == 'Linux': + if os.uname()[4][:3] == 'arm': + cpuinfo = subprocess.check_output(['cat', '/proc/cpuinfo']).decode() + if 'neon' in cpuinfo: + architecture = 'raspbian-armv7a' + else: + architecture = 'raspbian-armv6z' + else: + architecture = 'linux-x86_64' + + build_kwargs['libraries'] = ['FLAC-8.3.0'] + build_kwargs['library_dirs'] = [os.path.join(package_path, 'libraries', architecture)] + build_kwargs['extra_link_args'] = ['-Wl,-rpath,$ORIGIN/libraries/' + architecture] + + elif system == 'Windows': + if platform.architecture()[0] == '32bit': + architecture = 'windows-i686' + else: + architecture = 'windows-x86_64' + + build_kwargs['libraries'] = ['FLAC-8'] + build_kwargs['library_dirs'] = [os.path.join(package_path, 'libraries', architecture)] + else: + raise RuntimeError('%s platform is not supported' % system) + + return build_kwargs diff --git a/pyflac/builder/decoder.py b/pyflac/builder/decoder.py new file mode 100644 index 0000000..f222596 --- /dev/null +++ b/pyflac/builder/decoder.py @@ -0,0 +1,472 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC decoder builder +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import cffi +import os +import sys + +# flake8: noqa: E402 +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)))) +from build_args import get_build_kwargs + + +ffibuilder = cffi.FFI() +ffibuilder.set_source( + "pyflac._decoder", + """ + #include + #include + """, + **get_build_kwargs() +) + +# flake8: noqa: E501 +ffibuilder.cdef(""" + +// TYPES +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; + +typedef int FLAC__bool; +typedef FLAC__uint8 FLAC__byte; + +// ENUMS +typedef enum { + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + FLAC__STREAM_DECODER_READ_METADATA, + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + FLAC__STREAM_DECODER_READ_FRAME, + FLAC__STREAM_DECODER_END_OF_STREAM, + FLAC__STREAM_DECODER_OGG_ERROR, + FLAC__STREAM_DECODER_SEEK_ERROR, + FLAC__STREAM_DECODER_ABORTED, + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + FLAC__STREAM_DECODER_UNINITIALIZED +} FLAC__StreamDecoderState; + +extern const char * const FLAC__StreamDecoderStateString[]; + +typedef enum { + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED +} FLAC__StreamDecoderInitStatus; + +extern const char * const FLAC__StreamDecoderInitStatusString[]; + +typedef enum { + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + FLAC__STREAM_DECODER_READ_STATUS_ABORT +} FLAC__StreamDecoderReadStatus; + +typedef enum { + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED +} FLAC__StreamDecoderSeekStatus; + +typedef enum { + FLAC__STREAM_DECODER_TELL_STATUS_OK, + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED +} FLAC__StreamDecoderTellStatus; + +typedef enum { + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED +} FLAC__StreamDecoderLengthStatus; + +typedef enum { + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT +} FLAC__StreamDecoderWriteStatus; + +typedef enum { + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM +} FLAC__StreamDecoderErrorStatus; + +extern const char * const FLAC__StreamDecoderErrorStatusString[]; + +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 +} FLAC__EntropyCodingMethodType; + +// STRUCTURES +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +typedef struct { + uint32_t blocksize; + uint32_t sample_rate; + uint32_t channels; + FLAC__ChannelAssignment channel_assignment; + uint32_t bits_per_sample; + FLAC__FrameNumberType number_type; + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + FLAC__uint8 crc; +} FLAC__FrameHeader; + +typedef struct { + uint32_t *parameters; + uint32_t *raw_bits; + uint32_t capacity_by_order; +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +typedef struct { + uint32_t order; + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; +} FLAC__EntropyCodingMethod_PartitionedRice; + +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + uint32_t order; + FLAC__int32 warmup[4]; + const FLAC__int32 *residual; +} FLAC__Subframe_Fixed; + +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + uint32_t order; + uint32_t qlp_coeff_precision; + int quantization_level; + FLAC__int32 qlp_coeff[32]; + FLAC__int32 warmup[32]; + const FLAC__int32 *residual; +} FLAC__Subframe_LPC; + +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + uint32_t wasted_bits; +} FLAC__Subframe; + +typedef struct { + FLAC__uint16 crc; +} FLAC__FrameFooter; + +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[8]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +// METADATA +typedef enum { + FLAC__METADATA_TYPE_STREAMINFO = 0, + FLAC__METADATA_TYPE_PADDING = 1, + FLAC__METADATA_TYPE_APPLICATION = 2, + FLAC__METADATA_TYPE_SEEKTABLE = 3, + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + FLAC__METADATA_TYPE_CUESHEET = 5, + FLAC__METADATA_TYPE_PICTURE = 6, + FLAC__METADATA_TYPE_UNDEFINED = 7, + FLAC__MAX_METADATA_TYPE = 126, +} FLAC__MetadataType; + +typedef struct { + uint32_t min_blocksize, max_blocksize; + uint32_t min_framesize, max_framesize; + uint32_t sample_rate; + uint32_t channels; + uint32_t bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +typedef struct { + int dummy; +} FLAC__StreamMetadata_Padding; + +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +typedef struct { + FLAC__uint64 sample_number; + FLAC__uint64 stream_offset; + uint32_t frame_samples; +} FLAC__StreamMetadata_SeekPoint; + +typedef struct { + uint32_t num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +typedef struct { + FLAC__uint64 offset; + FLAC__byte number; +} FLAC__StreamMetadata_CueSheet_Index; + +typedef struct { + FLAC__uint64 offset; + FLAC__byte number; + char isrc[13]; + uint32_t type:1; + uint32_t pre_emphasis:1; + FLAC__byte num_indices; + FLAC__StreamMetadata_CueSheet_Index *indices; +} FLAC__StreamMetadata_CueSheet_Track; + +typedef struct { + char media_catalog_number[129]; + FLAC__uint64 lead_in; + FLAC__bool is_cd; + uint32_t num_tracks; + FLAC__StreamMetadata_CueSheet_Track *tracks; +} FLAC__StreamMetadata_CueSheet; + +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + char *mime_type; + FLAC__byte *description; + FLAC__uint32 width; + FLAC__uint32 height; + FLAC__uint32 depth; + FLAC__uint32 colors; + FLAC__uint32 data_length; + FLAC__byte *data; +} FLAC__StreamMetadata_Picture; + +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + +typedef struct { + FLAC__MetadataType type; + FLAC__bool is_last; + uint32_t length; + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; +} FLAC__StreamMetadata; + +// CALLBACKS +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +extern "Python" FLAC__StreamDecoderReadStatus _read_callback(const FLAC__StreamDecoder *, FLAC__byte *, size_t *, void *); +extern "Python" FLAC__StreamDecoderSeekStatus _seek_callback(const FLAC__StreamDecoder *, FLAC__uint64, void *); +extern "Python" FLAC__StreamDecoderTellStatus _tell_callback(const FLAC__StreamDecoder *, FLAC__uint64 *, void *); +extern "Python" FLAC__StreamDecoderLengthStatus _length_callback(const FLAC__StreamDecoder *, FLAC__uint64 *, void *); +extern "Python" FLAC__bool _eof_callback(const FLAC__StreamDecoder *, void *); +extern "Python" FLAC__StreamDecoderWriteStatus _write_callback(const FLAC__StreamDecoder *, const FLAC__Frame *, const FLAC__int32 const **, void *); +extern "Python" void _metadata_callback(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *, void *); +extern "Python" void _error_callback(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus, void *); + +// CONSTRUCTOR / DESTRUCTOR +FLAC__StreamDecoder *FLAC__stream_decoder_new(void); +void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + +// SETTERS +FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); +FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); +FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); +FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); +FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); +FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +// GETTERS +FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); +const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); +FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); +uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); +FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); +uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); +uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); +uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +// PROCESSING +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); +FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); +FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + + +""") + + +if __name__ == "__main__": + ffibuilder.compile(verbose=True) diff --git a/pyflac/builder/encoder.py b/pyflac/builder/encoder.py new file mode 100644 index 0000000..b622b3a --- /dev/null +++ b/pyflac/builder/encoder.py @@ -0,0 +1,325 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC encoder builder +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import cffi +import os +import sys + +# flake8: noqa: E402 +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)))) + +from build_args import get_build_kwargs + + +ffibuilder = cffi.FFI() +ffibuilder.set_source( + "pyflac._encoder", + """ + #include + #include + #include + """, + **get_build_kwargs() +) + +# flake8: noqa: E501 +ffibuilder.cdef(""" + +// TYPES +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; + +typedef int FLAC__bool; +typedef FLAC__uint8 FLAC__byte; + +// ENUMS +typedef enum { + FLAC__STREAM_ENCODER_OK = 0, + FLAC__STREAM_ENCODER_UNINITIALIZED, + FLAC__STREAM_ENCODER_OGG_ERROR, + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + FLAC__STREAM_ENCODER_CLIENT_ERROR, + FLAC__STREAM_ENCODER_IO_ERROR, + FLAC__STREAM_ENCODER_FRAMING_ERROR, + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR +} FLAC__StreamEncoderState; + +extern const char * const FLAC__StreamEncoderStateString[]; + +typedef enum { + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED +} FLAC__StreamEncoderInitStatus; + +extern const char * const FLAC__StreamEncoderInitStatusString[]; + +typedef enum { + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED +} FLAC__StreamEncoderReadStatus; + +typedef enum { + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED +} FLAC__StreamEncoderSeekStatus; + +typedef enum { + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED +} FLAC__StreamEncoderTellStatus; + +typedef enum { + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR +} FLAC__StreamEncoderWriteStatus; + +// STRUCTURES +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; + struct FLAC__StreamEncoderPrivate *private_; +} FLAC__StreamEncoder; + +// METADATA +typedef enum { + FLAC__METADATA_TYPE_STREAMINFO = 0, + FLAC__METADATA_TYPE_PADDING = 1, + FLAC__METADATA_TYPE_APPLICATION = 2, + FLAC__METADATA_TYPE_SEEKTABLE = 3, + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + FLAC__METADATA_TYPE_CUESHEET = 5, + FLAC__METADATA_TYPE_PICTURE = 6, + FLAC__METADATA_TYPE_UNDEFINED = 7, + FLAC__MAX_METADATA_TYPE = 126, +} FLAC__MetadataType; + +typedef struct { + uint32_t min_blocksize, max_blocksize; + uint32_t min_framesize, max_framesize; + uint32_t sample_rate; + uint32_t channels; + uint32_t bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +typedef struct { + int dummy; +} FLAC__StreamMetadata_Padding; + +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +typedef struct { + FLAC__uint64 sample_number; + FLAC__uint64 stream_offset; + uint32_t frame_samples; +} FLAC__StreamMetadata_SeekPoint; + +typedef struct { + uint32_t num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +typedef struct { + FLAC__uint64 offset; + FLAC__byte number; +} FLAC__StreamMetadata_CueSheet_Index; + +typedef struct { + FLAC__uint64 offset; + FLAC__byte number; + char isrc[13]; + uint32_t type:1; + uint32_t pre_emphasis:1; + FLAC__byte num_indices; + FLAC__StreamMetadata_CueSheet_Index *indices; +} FLAC__StreamMetadata_CueSheet_Track; + +typedef struct { + char media_catalog_number[129]; + FLAC__uint64 lead_in; + FLAC__bool is_cd; + uint32_t num_tracks; + FLAC__StreamMetadata_CueSheet_Track *tracks; +} FLAC__StreamMetadata_CueSheet; + +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + char *mime_type; + FLAC__byte *description; + FLAC__uint32 width; + FLAC__uint32 height; + FLAC__uint32 depth; + FLAC__uint32 colors; + FLAC__uint32 data_length; + FLAC__byte *data; +} FLAC__StreamMetadata_Picture; + +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + +typedef struct { + FLAC__MetadataType type; + FLAC__bool is_last; + uint32_t length; + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; +} FLAC__StreamMetadata; + +// CALLBACKS +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data); +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, uint32_t frames_written, uint32_t total_frames_estimate, void *client_data); + +extern "Python" FLAC__StreamEncoderReadStatus _read_callback(const FLAC__StreamEncoder *, FLAC__byte *, size_t *, void *); +extern "Python" FLAC__StreamEncoderWriteStatus _write_callback(const FLAC__StreamEncoder *, const FLAC__byte *, size_t, uint32_t, uint32_t, void *); +extern "Python" FLAC__StreamEncoderSeekStatus _seek_callback(const FLAC__StreamEncoder *, FLAC__uint64, void *); +extern "Python" FLAC__StreamEncoderTellStatus _tell_callback(const FLAC__StreamEncoder *, FLAC__uint64 *, void *); +extern "Python" void _metadata_callback(const FLAC__StreamEncoder *, const FLAC__StreamMetadata *, void *); +extern "Python" void _progress_callback(const FLAC__StreamEncoder *, FLAC__uint64, FLAC__uint64, uint32_t, uint32_t, void *); + +// CONSTRUCTOR / DESTRUCTOR +FLAC__StreamEncoder *FLAC__stream_encoder_new(void); +void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + +// SETTERS +FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *encoder, const char *specification); +FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_do_qlp_coeff_prec_search(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, uint32_t value); +FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +// GETTERS +FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); +const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); +void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, uint32_t *frame_number, uint32_t *channel, uint32_t *sample, FLAC__int32 *expected, FLAC__int32 *got); +FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_do_mid_side_stereo(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_do_qlp_coeff_prec_search(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_do_escape_coding(const FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder); +uint32_t FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder); +FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC__StreamEncoder *encoder); + +// PROCESSING +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderWriteCallback write_callback, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderTellCallback tell_callback, FLAC__StreamEncoderMetadataCallback metadata_callback, void *client_data); +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderReadCallback read_callback, FLAC__StreamEncoderWriteCallback write_callback, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderTellCallback tell_callback, FLAC__StreamEncoderMetadataCallback metadata_callback, void *client_data); +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE(FLAC__StreamEncoder *encoder, FILE *file, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data); +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE(FLAC__StreamEncoder *encoder, FILE *file, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data); +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file(FLAC__StreamEncoder *encoder, const char *filename, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data); +FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file(FLAC__StreamEncoder *encoder, const char *filename, FLAC__StreamEncoderProgressCallback progress_callback, void *client_data); +FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder); +FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], uint32_t samples); +FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], uint32_t samples); + +""") + + +if __name__ == "__main__": + ffibuilder.compile(verbose=True) diff --git a/pyflac/decoder.py b/pyflac/decoder.py new file mode 100644 index 0000000..cb096a7 --- /dev/null +++ b/pyflac/decoder.py @@ -0,0 +1,449 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC decoder +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +from collections import deque +from enum import Enum +import logging +from pathlib import Path +import tempfile +import threading +import time +from typing import Callable, Tuple + +import numpy as np +import soundfile as sf + +from pyflac._decoder import ffi as _ffi +from pyflac._decoder import lib as _lib + + +# -- State + +class DecoderState(Enum): + """ + The decoder state as a Python enumeration + """ + SEARCH_FOR_METADATA = _lib.FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + READ_METADATA = _lib.FLAC__STREAM_DECODER_READ_METADATA + SEARCH_FOR_FRAME_SYNC = _lib.FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC + READ_FRAME = _lib.FLAC__STREAM_DECODER_READ_FRAME + END_OF_STREAM = _lib.FLAC__STREAM_DECODER_END_OF_STREAM + OGG_ERROR = _lib.FLAC__STREAM_DECODER_OGG_ERROR + SEEK_ERROR = _lib.FLAC__STREAM_DECODER_SEEK_ERROR + ABORTED = _lib.FLAC__STREAM_DECODER_ABORTED + MEMORY_ALLOCATION_ERROR = _lib.FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR + UNINITIALIZED = _lib.FLAC__STREAM_DECODER_UNINITIALIZED + + def __str__(self): + return _ffi.string(_lib.FLAC__StreamDecoderStateString[self.value]).decode() + + +# -- Exceptions + +class DecoderInitException(Exception): + """ + An exception raised if initialisation fails for a + `StreamDecoder` or a `FileDecoder`. + """ + def __init__(self, code): + self.code = code + + def __str__(self): + return _ffi.string(_lib.FLAC__StreamDecoderInitStatusString[self.code]).decode() + + +class DecoderProcessException(Exception): + """ + An exception raised if an error occurs during the + processing of audio data. + """ + pass + + +# -- Decoder + +class _Decoder: + """ + A pyFLAC decoder. + + This generic class handles interaction with libFLAC. + """ + def __init__(self): + """ + Create a new libFLAC instance. + This instance is automatically released when there are no more references to the decoder. + """ + self._error = None + self._decoder = _ffi.gc(_lib.FLAC__stream_decoder_new(), _lib.FLAC__stream_decoder_delete) + self._decoder_handle = _ffi.new_handle(self) + self.logger = logging.getLogger(__name__) + + def finish(self): + """ + Finish the decoding process. + + Flushes the decoding buffer, releases resources, resets the decoder + settings to their defaults, and returns the decoder state to `DecoderState.UNINITIALIZED`. + + A well behaved program should always call this at the end. + """ + _lib.FLAC__stream_decoder_finish(self._decoder) + + # -- State + + @property + def state(self) -> DecoderState: + """ + DecoderState: Property to return the decoder state + """ + return DecoderState(_lib.FLAC__stream_decoder_get_state(self._decoder)) + + # -- Processing + + def process(self): + raise NotImplementedError + + +class StreamDecoder(_Decoder): + """ + A pyFLAC stream decoder converts a stream of FLAC encoded bytes + back to raw audio data. + + The compressed data is passed in via the `process` method, and + blocks of raw uncompressed audio is passed back to the user via + the `callback`. + + Args: + callback (fn): Function to call when there is uncompressed + audio data ready, see the example below for more information. + + Examples: + An example callback which writes the audio data to file + using SoundFile. + + .. code-block:: python + :linenos: + + import soundfile as sf + + def callback(self, + audio: np.ndarray, + sample_rate: int, + num_channels: int, + num_samples: int): + + # ------------------------------------------------------ + # Note: num_samples is the number of samples per channel + # ------------------------------------------------------ + if self.output is None: + self.output = sf.SoundFile( + 'output.wav', mode='w', channels=num_channels, + samplerate=sample_rate + ) + self.output.write(audio) + + Raises: + DecoderInitException: If initialisation of the decoder fails + """ + def __init__(self, + callback: Callable[[np.ndarray, int, int, int], None]): + super().__init__() + + self._done = False + self._buffer = deque() + self.callback = callback + + rc = _lib.FLAC__stream_decoder_init_stream( + self._decoder, + _lib._read_callback, + _ffi.NULL, + _ffi.NULL, + _ffi.NULL, + _ffi.NULL, + _lib._write_callback, + _ffi.NULL, + _lib._error_callback, + self._decoder_handle + ) + if rc != _lib.FLAC__STREAM_DECODER_INIT_STATUS_OK: + raise DecoderInitException(rc) + + self._thread = threading.Thread(target=self._process) + self._thread.daemon = True + self._thread.start() + + # -- Processing + + def _process(self): + """ + Internal function to instruct the decoder to process until the end of + the stream. This should be run in a separate thread. + """ + if not _lib.FLAC__stream_decoder_process_until_end_of_stream(self._decoder): + self._error = 'A fatal read, write, or memory allocation error occurred' + + def process(self, data: bytes): + """ + Instruct the decoder to process some data. + + Note: This is a non-blocking function, data is processed in + a background thread. + + Args: + data (bytes): Bytes of FLAC data + """ + self._buffer.append(data) + + def finish(self): + """ + Finish the decoding process. + + This must be called at the end of the decoding process. + + Flushes the decoding buffer, closes the processing thread, releases resources, resets the decoder + settings to their defaults, and returns the decoder state to `DecoderState.UNINITIALIZED`. + + Raises: + DecoderProcessException: if any fatal read, write, or memory allocation + error occurred. + """ + # -------------------------------------------------------------- + # Finish processing what's in the buffer if there are no errors + # -------------------------------------------------------------- + while self._thread.is_alive() and self._error is None and len(self._buffer) > 0: + time.sleep(0.01) + + # -------------------------------------------------------------- + # Instruct the decoder to finish up and wait until it is done + # -------------------------------------------------------------- + self._done = True + super().finish() + self._thread.join(timeout=3) + if self._error: + raise DecoderProcessException(self._error) + + +class FileDecoder(_Decoder): + """ + The pyFLAC file decoder reads the encoded audio data directly from a FLAC + file and writes to a WAV file. + + Args: + input_file (pathlib.Path): Path to the input FLAC file + output_file (pathlib.Path): Path to the output WAV file, a temporary + file will be created if unspecified. + + Raises: + DecoderInitException: If initialisation of the decoder fails + """ + def __init__(self, + input_file: Path, + output_file: Path = None): + super().__init__() + + self.__output = None + self.callback = self._callback + if output_file: + self.__output_file = output_file + else: + output_file = tempfile.NamedTemporaryFile(suffix='.wav') + self.__output_file = Path(output_file.name) + + c_input_filename = _ffi.new('char[]', str(input_file).encode('utf-8')) + rc = _lib.FLAC__stream_decoder_init_file( + self._decoder, + c_input_filename, + _lib._write_callback, + _ffi.NULL, + _lib._error_callback, + self._decoder_handle, + ) + _ffi.release(c_input_filename) + if rc != _lib.FLAC__STREAM_DECODER_INIT_STATUS_OK: + raise DecoderInitException(rc) + + def process(self) -> Tuple[np.ndarray, int]: + """ + Process the audio data from the FLAC file. + + Returns: + (tuple): A tuple of the decoded numpy audio array, and the sample rate of the audio data. + + Raises: + DecoderProcessException: if any fatal read, write, or memory allocation + error occurred (meaning decoding must stop) + """ + result = _lib.FLAC__stream_decoder_process_until_end_of_stream(self._decoder) + if self.state != DecoderState.END_OF_STREAM and not result: + raise DecoderProcessException(str(self.state)) + + self.finish() + + if self.__output: + self.__output.close() + return sf.read(str(self.__output_file), always_2d=True) + + def _callback(self, data: np.ndarray, sample_rate: int, num_channels: int, num_samples: int): + """ + Internal callback to write the decoded data to a WAV file. + """ + if self.__output is None: + self.__output = sf.SoundFile( + self.__output_file, mode='w', channels=num_channels, + samplerate=sample_rate + ) + self.__output.write(data) + + +@_ffi.def_extern(error=_lib.FLAC__STREAM_DECODER_READ_STATUS_ABORT) +def _read_callback(_decoder, + byte_buffer, + num_bytes, + client_data): + """ + Called internally when the decoder needs more input data. + + If an exception is raised here, the abort status is returned. + """ + decoder = _ffi.from_handle(client_data) + if decoder._error: + # ---------------------------------------------------------- + # If an error has been issued via the error callback, then + # abort the processing of the stream. + # ---------------------------------------------------------- + return _lib.FLAC__STREAM_DECODER_READ_STATUS_ABORT + + if decoder._done: + # ---------------------------------------------------------- + # The end of the stream has been instructed by a call to + # finish. + # ---------------------------------------------------------- + num_bytes[0] = 0 + return _lib.FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM + + maximum_bytes = int(num_bytes[0]) + while len(decoder._buffer) == 0: + # ---------------------------------------------------------- + # Wait until there is something in the buffer + # ---------------------------------------------------------- + time.sleep(0.01) + + # -------------------------------------------------------------- + # Ensure only the maximum bytes or less is taken from + # the thread safe queue. + # -------------------------------------------------------------- + data = bytes() + if len(decoder._buffer[0]) <= maximum_bytes: + data = decoder._buffer.popleft() + maximum_bytes -= len(data) + + if len(decoder._buffer) > 0 and len(decoder._buffer[0]) > maximum_bytes: + data += decoder._buffer[0][0:maximum_bytes] + decoder._buffer[0] = decoder._buffer[0][maximum_bytes:] + + actual_bytes = len(data) + num_bytes[0] = actual_bytes + _ffi.memmove(byte_buffer, data, actual_bytes) + return _lib.FLAC__STREAM_DECODER_READ_STATUS_CONTINUE + + +@_ffi.def_extern() +def _seek_callback(_decoder, + absolute_byte_offset, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _tell_callback(_decoder, + absolute_byte_offset, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _length_callback(_decoder, + stream_length, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _eof_callback(_decoder, + client_data): + raise NotImplementedError + + +@_ffi.def_extern(error=_lib.FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) +def _write_callback(_decoder, + frame, + buffer, + client_data): + """ + Called internally when the decoder has uncompressed + raw audio data to output. + + If an exception is raised here, the abort status is returned. + """ + channels = [] + decoder = _ffi.from_handle(client_data) + + # -------------------------------------------------------------- + # Data comes from libFLAC in a 32bit array, where the 16bit + # audio data sits in the least significant bits. + # -------------------------------------------------------------- + bytes_per_frame = frame.header.blocksize * np.dtype(np.int32).itemsize + + if frame.header.bits_per_sample != 16: + raise ValueError('Only int16 data type is supported') + + # -------------------------------------------------------------- + # The buffer contains an array of pointers to decoded channels + # of data. Each pointer will point to an array of signed samples + # of length `frame.header.blocksize`. + # + # Channels will be ordered according to the FLAC specification. + # -------------------------------------------------------------- + for ch in range(0, frame.header.channels): + cbuffer = _ffi.buffer(buffer[ch], bytes_per_frame) + channels.append( + np.frombuffer(cbuffer, dtype='int32').astype(np.int16) + ) + output = np.column_stack(channels) + decoder.callback( + output, + int(frame.header.sample_rate), + int(frame.header.channels), + int(frame.header.blocksize) + ) + + return _lib.FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE + + +@_ffi.def_extern() +def _metadata_callback(_decoder, + metadata, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _error_callback(_decoder, + status, + client_data): + """ + Called whenever an error occurs during decoding. + """ + decoder = _ffi.from_handle(client_data) + message = _ffi.string( + _lib.FLAC__StreamDecoderErrorStatusString[status]).decode() + decoder.logger.error(f'Error in libFLAC decoder: {message}') + decoder._error = message diff --git a/pyflac/encoder.py b/pyflac/encoder.py new file mode 100644 index 0000000..e53de0e --- /dev/null +++ b/pyflac/encoder.py @@ -0,0 +1,434 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC encoder +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +from enum import Enum +import logging +from pathlib import Path +import tempfile +from typing import Callable + +import numpy as np +import soundfile as sf + +from pyflac._encoder import ffi as _ffi +from pyflac._encoder import lib as _lib + + +# -- State + +class EncoderState(Enum): + """ + The encoder state as a Python enumeration + """ + OK = _lib.FLAC__STREAM_ENCODER_OK + UNINITIALIZED = _lib.FLAC__STREAM_ENCODER_UNINITIALIZED + OGG_ERROR = _lib.FLAC__STREAM_ENCODER_OGG_ERROR + VERIFY_DECODER_ERROR = _lib.FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR + VERIFY_MISMATCH_IN_AUDIO_DATA = _lib.FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA + CLIENT_ERROR = _lib.FLAC__STREAM_ENCODER_CLIENT_ERROR + IO_ERROR = _lib.FLAC__STREAM_ENCODER_IO_ERROR + FRAMING_ERROR = _lib.FLAC__STREAM_ENCODER_FRAMING_ERROR + MEMORY_ALLOCATION_ERROR = _lib.FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + + def __str__(self): + return _ffi.string(_lib.FLAC__StreamEncoderStateString[self.value]).decode() + + +class EncoderInitException(Exception): + """ + An exception raised if initialisation fails for a + `StreamEncoder` or a `FileEncoder`. + """ + def __init__(self, code): + self.code = code + + def __str__(self): + return _ffi.string(_lib.FLAC__StreamEncoderInitStatusString[self.code]).decode() + + +class EncoderProcessException(Exception): + """ + An exception raised if an error occurs during the + processing of audio data. + """ + pass + + +class _Encoder: + """ + A pyFLAC Encoder. + + This generic class handles interaction with libFLAC. + """ + def __init__(self): + """ + Create a new libFLAC instance. + This instance is automatically released when there are no more references to the encoder. + """ + self._initialised = False + self._encoder = _ffi.gc(_lib.FLAC__stream_encoder_new(), _lib.FLAC__stream_encoder_delete) + self._encoder_handle = _ffi.new_handle(self) + self.logger = logging.getLogger(__name__) + + def _init(self): + raise NotImplementedError + + # -- Processing + + def process(self, samples: np.ndarray): + """ + Process some samples. + + This method ensures the samples are contiguous in memory and then + passes a pointer to the numpy array to the FLAC encoder to process. + + On processing the first buffer of samples, the encoder is set up + for the given amount of channels and data type. This is automatically + determined from the numpy array. + + Raises: + TypeError: if a numpy array of samples is not provided + EncoderProcessException: if an error occurs when processing the samples + """ + if not isinstance(samples, np.ndarray): + raise TypeError('Processing only supports numpy arrays') + + if not self._initialised: + try: + self._channels = samples.shape[1] + except IndexError: + self._channels = 1 + self._bits_per_sample = samples.dtype.itemsize * 8 + self._init() + + samples = np.ascontiguousarray(samples).astype(np.int32) + samples_ptr = _ffi.from_buffer('int32_t[]', samples) + + result = _lib.FLAC__stream_encoder_process_interleaved(self._encoder, samples_ptr, len(samples)) + _ffi.release(samples_ptr) + + if not result: + raise EncoderProcessException(str(self.state)) + + def finish(self) -> bool: + """ + Finish the encoding process. This flushes the encoding buffer, + releases resources, resets the encoder settings to their defaults, + and returns the encoder state to `EncoderState.UNINITIALIZED`. + + A well behaved program should always call this at the end. + + Returns: + (bool): `True` if successful, `False` otherwise. + """ + return _lib.FLAC__stream_encoder_finish(self._encoder) + + # -- State + + @property + def state(self) -> EncoderState: + """ + EncoderState: Property to return the encoder state + """ + return EncoderState(_lib.FLAC__stream_encoder_get_state(self._encoder)) + + # -- Getters & Setters + + @property + def _verify(self) -> bool: + """ + bool: Property to get/set the encoder verify functionality. + If set `True`, the encoder will verify its own encoded output + by feeding it through an internal decoder and comparing + the original signal against the decoded signal. + """ + return _lib.FLAC__stream_encoder_get_verify(self._encoder) + + @_verify.setter + def _verify(self, value: bool): + _lib.FLAC__stream_encoder_set_verify(self._encoder, bool(value)) + + @property + def _channels(self) -> int: + """ + int: Property to get/set the number of audio channels to encode. + """ + return _lib.FLAC__stream_encoder_get_channels(self._encoder) + + @_channels.setter + def _channels(self, value: int): + _lib.FLAC__stream_encoder_set_channels(self._encoder, value) + + @property + def _bits_per_sample(self) -> int: + """ + int: Property to get/set the resolution of the input to be encoded. + """ + return _lib.FLAC__stream_encoder_get_bits_per_sample(self._encoder) + + @_bits_per_sample.setter + def _bits_per_sample(self, value: int): + _lib.FLAC__stream_encoder_set_bits_per_sample(self._encoder, value) + + @property + def _sample_rate(self) -> int: + """ + int: Property to get/set the sample rate (in Hz) of the input to be encoded. + """ + return _lib.FLAC__stream_encoder_get_sample_rate(self._encoder) + + @_sample_rate.setter + def _sample_rate(self, value: int): + _lib.FLAC__stream_encoder_set_sample_rate(self._encoder, value) + + @property + def _blocksize(self) -> int: + """ + int: Property to get/set the number of samples to use per frame. + Use `0` to let the encoder estimate a blocksize - this is usually best. + """ + return _lib.FLAC__stream_encoder_get_blocksize(self._encoder) + + @_blocksize.setter + def _blocksize(self, value: int): + _lib.FLAC__stream_encoder_set_blocksize(self._encoder, value) + + @property + def _compression_level(self) -> int: + raise NotImplementedError + + @_compression_level.setter + def _compression_level(self, value: int): + _lib.FLAC__stream_encoder_set_compression_level(self._encoder, value) + + +class StreamEncoder(_Encoder): + """ + The pyFLAC stream encoder is used for real-time compression of + raw audio data. + + Raw audio data is passed in via the `process` method, and chunks + of compressed data is passed back to the user via the `callback`. + + Args: + sample_rate (int): The raw audio sample rate (Hz) + callback (fn): Function to call when there is compressed + data ready, see the example below for more information. + compression_level (int): The compression level parameter that + varies from 0 (fastest) to 8 (slowest). The default setting + is 5, see https://en.wikipedia.org/wiki/FLAC for more details. + blocksize (int): The size of the block to be returned in the + callback. The default is 0 which allows libFLAC to determine + the best block size. + verify (bool): If `True`, the encoder will verify its own + encoded output by feeding it through an internal decoder and + comparing the original signal against the decoded signal. + If a mismatch occurs, the `process` method will raise a + `EncoderProcessException`. Note that this will slow the + encoding process by the extra time required for decoding and comparison. + + Examples: + An example callback which adds the encoded data to a queue for + later processing. + + .. code-block:: python + :linenos: + + def callback(self, + buffer: bytes, + num_bytes: int, + num_samples: int, + current_frame: int): + if num_samples == 0: + # If there are no samples in the encoded data, this is + # a FLAC header. The header data will arrive in several + # different callbacks. Otherwise `num_samples` will be + # the block size value. + pass + + self.queue.append(buffer) + self.total_bytes += num_bytes + + Raises: + ValueError: If any invalid values are passed in to the constructor. + """ + def __init__(self, + sample_rate: int, + callback: Callable[[bytes, int, int, int], None], + compression_level: int = 5, + blocksize: int = 0, + verify: bool = False): + super().__init__() + + self.callback = callback + + self._sample_rate = sample_rate + self._blocksize = blocksize + self._compression_level = compression_level + self._verify = verify + + def _init(self): + rc = _lib.FLAC__stream_encoder_init_stream( + self._encoder, + _lib._write_callback, + _ffi.NULL, + _ffi.NULL, + _ffi.NULL, + self._encoder_handle + ) + if rc != _lib.FLAC__STREAM_ENCODER_INIT_STATUS_OK: + raise EncoderInitException(rc) + + self._initialised = True + + +class FileEncoder(_Encoder): + """ + The pyFLAC file encoder reads the raw audio data from the WAV file and + writes the encoded audio data to a FLAC file. + + Args: + input_file (pathlib.Path): Path to the input WAV file + output_file (pathlib.Path): Path to the output FLAC file, a temporary + file will be created if unspecified. + compression_level (int): The compression level parameter that + varies from 0 (fastest) to 8 (slowest). The default setting + is 5, see https://en.wikipedia.org/wiki/FLAC for more details. + blocksize (int): The size of the block to be returned in the + callback. The default is 0 which allows libFLAC to determine + the best block size. + verify (bool): If `True`, the encoder will verify its own + encoded output by feeding it through an internal decoder and + comparing the original signal against the decoded signal. + If a mismatch occurs, the `process` method will raise a + `EncoderProcessException`. Note that this will slow the + encoding process by the extra time required for decoding and comparison. + + Raises: + ValueError: If any invalid values are passed in to the constructor. + """ + def __init__(self, + input_file: Path, + output_file: Path = None, + compression_level: int = 5, + blocksize: int = 0, + verify: bool = False): + super().__init__() + + self.__raw_audio, sample_rate = sf.read(str(input_file), dtype='int16') + if output_file: + self.__output_file = output_file + else: + output_file = tempfile.NamedTemporaryFile(suffix='.flac') + self.__output_file = Path(output_file.name) + + self._sample_rate = sample_rate + self._blocksize = blocksize + self._compression_level = compression_level + self._verify = verify + + def _init(self): + """ + Initialise the encoder to write to a file. + + Raises: + EncoderInitException: if initialisation fails. + """ + c_output_filename = _ffi.new('char[]', str(self.__output_file).encode('utf-8')) + rc = _lib.FLAC__stream_encoder_init_file( + self._encoder, + c_output_filename, + _lib._progress_callback, + self._encoder_handle, + ) + _ffi.release(c_output_filename) + if rc != _lib.FLAC__STREAM_ENCODER_INIT_STATUS_OK: + raise EncoderInitException(rc) + + self._initialised = True + + def process(self) -> bytes: + """ + Process the audio data from the WAV file. + + Returns: + (bytes): The FLAC encoded bytes. + + Raises: + EncoderProcessException: if an error occurs when processing the samples + """ + super().process(self.__raw_audio) + self.finish() + with open(self.__output_file, 'rb') as f: + return f.read() + + +@_ffi.def_extern(error=_lib.FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR) +def _write_callback(_encoder, + byte_buffer, + num_bytes, + num_samples, + current_frame, + client_data): + """ + Called internally when the encoder has compressed + data ready to write. + + If an exception is raised here, the abort status is returned. + """ + encoder = _ffi.from_handle(client_data) + buffer = bytes(_ffi.buffer(byte_buffer, num_bytes)) + encoder.callback( + buffer, + num_bytes, + num_samples, + current_frame + ) + return _lib.FLAC__STREAM_ENCODER_WRITE_STATUS_OK + + +@_ffi.def_extern() +def _seek_callback(_encoder, + absolute_byte_offset, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _tell_callback(_encoder, + absolute_byte_offset, + client_data): + raise NotImplementedError + + +@_ffi.def_extern() +def _metadata_callback(_encoder, + metadata, + client_data): + """ + Called once at the end of encoding with the populated + STREAMINFO structure. This is so the client can seek back + to the beginning of the file and write the STREAMINFO block + with the correct statistics after encoding (like minimum/maximum + frame size and total samples). + """ + raise NotImplementedError + + +@_ffi.def_extern() +def _progress_callback(_encoder, + bytes_written, + samples_written, + frames_written, + total_frames_estimate, + client_data): + encoder = _ffi.from_handle(client_data) + encoder.logger.debug(f'{frames_written} frames written') diff --git a/pyflac/include/FLAC/export.h b/pyflac/include/FLAC/export.h new file mode 100644 index 0000000..d52f0bb --- /dev/null +++ b/pyflac/include/FLAC/export.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * If you are compiling with MSVC and will link to the static library + * (libFLAC.lib) you should define FLAC__NO_DLL in your project to + * make sure the symbols are exported properly. + * + * \{ + */ + +#if defined(FLAC__NO_DLL) +#define FLAC_API + +#elif defined(_MSC_VER) +#ifdef FLAC_API_EXPORTS +#define FLAC_API __declspec(dllexport) +#else +#define FLAC_API __declspec(dllimport) +#endif + +#elif defined(FLAC__USE_VISIBILITY_ATTR) +#define FLAC_API __attribute__ ((visibility ("default"))) + +#else +#define FLAC_API + +#endif + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 11 +#define FLAC_API_VERSION_REVISION 0 /**< see above */ +#define FLAC_API_VERSION_AGE 3 /**< see above */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ +extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; + +#ifdef __cplusplus +} +#endif + +/* \} */ + +#endif diff --git a/pyflac/include/FLAC/format.h b/pyflac/include/FLAC/format.h new file mode 100644 index 0000000..769ab8a --- /dev/null +++ b/pyflac/include/FLAC/format.h @@ -0,0 +1,1025 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#include "export.h" +#include "ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * FLAC format. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see FLAC format + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + /**< Residual is coded by partitioning into contexts, each with it's own + * 4-bit Rice parameter. */ + + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 + /**< Residual is coded by partitioning into contexts, each with it's own + * 5-bit Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + uint32_t *parameters; + /**< The Rice parameters for each context. */ + + uint32_t *raw_bits; + /**< Widths for escape-coded partitions. Will be non-zero for escaped + * partitions and zero for unescaped partitions. + */ + + uint32_t capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. format specification) + */ +typedef struct { + + uint32_t order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<format specification) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + + +/** CONSTANT subframe. (c.f. format specification) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + + +/** VERBATIM subframe. (c.f. format specification) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + + +/** FIXED subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + uint32_t order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + + +/** LPC subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + uint32_t order; + /**< The FIR order. */ + + uint32_t qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + + +/** FLAC subframe structure. (c.f. format specification) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + uint32_t wasted_bits; +} FLAC__Subframe; + +/** == 1 (bit) + * + * This used to be a zero-padding bit (hence the name + * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit. It still has a + * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 + * to mean something else. + */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + + +/** FLAC frame header structure. (c.f. format specification) + */ +typedef struct { + uint32_t blocksize; + /**< The number of samples per subframe. */ + + uint32_t sample_rate; + /**< The sample rate in Hz. */ + + uint32_t channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + uint32_t bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. As a convenience, the + * decoder will always convert a frame number to a sample number because + * the rules are complex. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + + +/** FLAC frame footer structure. (c.f. format specification) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + + +/** FLAC frame structure. (c.f. format specification) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< STREAMINFO block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< PADDING block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< APPLICATION block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< SEEKTABLE block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< CUESHEET block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< PICTURE block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7, + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + /**< No type will ever be greater than this. There is not enough room in the protocol block. */ +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + + +/** FLAC STREAMINFO structure. (c.f. format specification) + */ +typedef struct { + uint32_t min_blocksize, max_blocksize; + uint32_t min_framesize, max_framesize; + uint32_t sample_rate; + uint32_t channels; + uint32_t bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. format specification) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + + +/** FLAC APPLICATION structure. (c.f. format specification) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + uint32_t frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + + +/** FLAC SEEKTABLE structure. (c.f. format specification) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + uint32_t num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + + +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + + +/** FLAC CUESHEET track index structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + + +/** FLAC CUESHEET track structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + uint32_t type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + uint32_t pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + + +/** FLAC CUESHEET structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + uint32_t num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + + +/** FLAC metadata block structure. (c.f. format specification) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + uint32_t length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate); + +/** Tests that a blocksize at the given sample rate is valid for the FLAC + * subset. + * + * \param blocksize The blocksize to test for compliance. + * \param sample_rate The sample rate is needed, since the valid subset + * blocksize depends on the sample rate. + * \retval FLAC__bool + * \c true if the given blocksize conforms to the specification for the + * subset at the given sample rate, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (uint32_t)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length); + +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval uint32_t + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pyflac/include/FLAC/ordinals.h b/pyflac/include/FLAC/ordinals.h new file mode 100644 index 0000000..75b830d --- /dev/null +++ b/pyflac/include/FLAC/ordinals.h @@ -0,0 +1,85 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if defined(_MSC_VER) && _MSC_VER < 1600 + +/* Microsoft Visual Studio earlier than the 2010 version did not provide + * the 1999 ISO C Standard header file . + */ + +typedef signed __int8 FLAC__int8; +typedef signed __int16 FLAC__int16; +typedef signed __int32 FLAC__int32; +typedef signed __int64 FLAC__int64; +typedef unsigned __int8 FLAC__uint8; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; + +#else + +/* For MSVC 2010 and everything else which provides . */ + +#include + +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; + +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif diff --git a/pyflac/include/FLAC/stream_decoder.h b/pyflac/include/FLAC/stream_decoder.h new file mode 100644 index 0000000..57215c5 --- /dev/null +++ b/pyflac/include/FLAC/stream_decoder.h @@ -0,0 +1,1559 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read or write callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * frame header. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdin since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdin since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process_*() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pyflac/include/FLAC/stream_encoder.h b/pyflac/include/FLAC/stream_encoder.h new file mode 100644 index 0000000..d154ac4 --- /dev/null +++ b/pyflac/include/FLAC/stream_encoder.h @@ -0,0 +1,1790 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_ENCODER_H +#define FLAC__STREAM_ENCODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" +#include "stream_decoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_encoder.h + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * See the detailed documentation in the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_encoder FLAC/ \*_encoder.h: encoder interfaces + * \ingroup flac + * + * \brief + * This module describes the encoder layers provided by libFLAC. + * + * The stream encoder can be used to encode complete streams either to the + * client via callbacks, or directly to a file, depending on how it is + * initialized. When encoding via callbacks, the client provides a write + * callback which will be called whenever FLAC data is ready to be written. + * If the client also supplies a seek callback, the encoder will also + * automatically handle the writing back of metadata discovered while + * encoding, like stream info, seek points offsets, etc. When encoding to + * a file, the client needs only supply a filename or open \c FILE* and an + * optional progress callback for periodic notification of progress; the + * write and seek callbacks are supplied internally. For more info see the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_stream_encoder FLAC/stream_encoder.h: stream encoder interface + * \ingroup flac_encoder + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * The stream encoder can encode to native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this encoder is as follows: + * - The program creates an instance of an encoder using + * FLAC__stream_encoder_new(). + * - The program overrides the default settings using + * FLAC__stream_encoder_set_*() functions. At a minimum, the following + * functions should be called: + * - FLAC__stream_encoder_set_channels() + * - FLAC__stream_encoder_set_bits_per_sample() + * - FLAC__stream_encoder_set_sample_rate() + * - FLAC__stream_encoder_set_ogg_serial_number() (if encoding to Ogg FLAC) + * - FLAC__stream_encoder_set_total_samples_estimate() (if known) + * - If the application wants to control the compression level or set its own + * metadata, then the following should also be called: + * - FLAC__stream_encoder_set_compression_level() + * - FLAC__stream_encoder_set_verify() + * - FLAC__stream_encoder_set_metadata() + * - The rest of the set functions should only be called if the client needs + * exact control over how the audio is compressed; thorough understanding + * of the FLAC format is necessary to achieve good results. + * - The program initializes the instance to validate the settings and + * prepare for encoding using + * - FLAC__stream_encoder_init_stream() or FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file() for native FLAC + * - FLAC__stream_encoder_init_ogg_stream() or FLAC__stream_encoder_init_ogg_FILE() + * or FLAC__stream_encoder_init_ogg_file() for Ogg FLAC + * - The program calls FLAC__stream_encoder_process() or + * FLAC__stream_encoder_process_interleaved() to encode data, which + * subsequently calls the callbacks when there is encoder data ready + * to be written. + * - The program finishes the encoding with FLAC__stream_encoder_finish(), + * which causes the encoder to encode any data still in its input pipe, + * update the metadata with the final encoding statistics if output + * seeking is possible, and finally reset the encoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_encoder_delete(). + * + * In more detail, the stream encoder functions similarly to the + * \link flac_stream_decoder stream decoder \endlink, but has fewer + * callbacks and more options. Typically the client will create a new + * instance by calling FLAC__stream_encoder_new(), then set the necessary + * parameters with FLAC__stream_encoder_set_*(), and initialize it by + * calling one of the FLAC__stream_encoder_init_*() functions. + * + * Unlike the decoders, the stream encoder has many options that can + * affect the speed and compression ratio. When setting these parameters + * you should have some basic knowledge of the format (see the + * user-level documentation + * or the formal description). The + * FLAC__stream_encoder_set_*() functions themselves do not validate the + * values as many are interdependent. The FLAC__stream_encoder_init_*() + * functions will do this, so make sure to pay attention to the state + * returned by FLAC__stream_encoder_init_*() to make sure that it is + * FLAC__STREAM_ENCODER_INIT_STATUS_OK. Any parameters that are not set + * before FLAC__stream_encoder_init_*() will take on the defaults from + * the constructor. + * + * There are three initialization functions for native FLAC, one for + * setting up the encoder to encode FLAC data to the client via + * callbacks, and two for encoding directly to a file. + * + * For encoding via callbacks, use FLAC__stream_encoder_init_stream(). + * You must also supply a write callback which will be called anytime + * there is raw encoded data to write. If the client can seek the output + * it is best to also supply seek and tell callbacks, as this allows the + * encoder to go back after encoding is finished to write back + * information that was collected while encoding, like seek point offsets, + * frame sizes, etc. + * + * For encoding directly to a file, use FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file(). Then you must only supply a + * filename or open \c FILE*; the encoder will handle all the callbacks + * internally. You may also supply a progress callback for periodic + * notification of the encoding progress. + * + * There are three similarly-named init functions for encoding to Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * The call to FLAC__stream_encoder_init_*() currently will also immediately + * call the write callback several times, once with the \c fLaC signature, + * and once for each encoded metadata block. Note that for Ogg FLAC + * encoding you will usually get at least twice the number of callbacks than + * with native FLAC, one for the Ogg page header and one for the page body. + * + * After initializing the instance, the client may feed audio data to the + * encoder in one of two ways: + * + * - Channel separate, through FLAC__stream_encoder_process() - The client + * will pass an array of pointers to buffers, one for each channel, to + * the encoder, each of the same length. The samples need not be + * block-aligned, but each channel should have the same number of samples. + * - Channel interleaved, through + * FLAC__stream_encoder_process_interleaved() - The client will pass a single + * pointer to data that is channel-interleaved (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * Again, the samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 and + * the last value channelN_sampleM. + * + * Note that for either process call, each sample in the buffers should be a + * signed integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the resolution + * is 16 bits per sample, the samples should all be in the range [-32768,32767]. + * + * When the client is finished encoding data, it calls + * FLAC__stream_encoder_finish(), which causes the encoder to encode any + * data still in its input pipe, and call the metadata callback with the + * final encoding statistics. Then the instance may be deleted with + * FLAC__stream_encoder_delete() or initialized again to encode another + * stream. + * + * For programs that write their own metadata, but that do not know the + * actual metadata until after encoding, it is advantageous to instruct + * the encoder to write a PADDING block of the correct size, so that + * instead of rewriting the whole stream after encoding, the program can + * just overwrite the PADDING block. If only the maximum size of the + * metadata is known, the program can write a slightly larger padding + * block, then split it after encoding. + * + * Make sure you understand how lengths are calculated. All FLAC metadata + * blocks have a 4 byte header which contains the type and length. This + * length does not include the 4 bytes of the header. See the format page + * for the specification of metadata blocks and their lengths. + * + * \note + * If you are writing the FLAC data to a file via callbacks, make sure it + * is open for update (e.g. mode "w+" for stdio streams). This is because + * after the first encoding pass, the encoder will try to seek back to the + * beginning of the stream, to the STREAMINFO block, to write some data + * there. (If using FLAC__stream_encoder_init*_file() or + * FLAC__stream_encoder_init*_FILE(), the file is managed internally.) + * + * \note + * The "set" functions may only be called when the encoder is in the + * state FLAC__STREAM_ENCODER_UNINITIALIZED, i.e. after + * FLAC__stream_encoder_new() or FLAC__stream_encoder_finish(), but + * before FLAC__stream_encoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_encoder_finish() resets all settings to the constructor + * defaults. + * + * \{ + */ + + +/** State values for a FLAC__StreamEncoder. + * + * The encoder's state can be obtained by calling FLAC__stream_encoder_get_state(). + * + * If the encoder gets into any other state besides \c FLAC__STREAM_ENCODER_OK + * or \c FLAC__STREAM_ENCODER_UNINITIALIZED, it becomes invalid for encoding and + * must be deleted with FLAC__stream_encoder_delete(). + */ +typedef enum { + + FLAC__STREAM_ENCODER_OK = 0, + /**< The encoder is in the normal OK state and samples can be processed. */ + + FLAC__STREAM_ENCODER_UNINITIALIZED, + /**< The encoder is in the uninitialized state; one of the + * FLAC__stream_encoder_init_*() functions must be called before samples + * can be processed. + */ + + FLAC__STREAM_ENCODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + /**< An error occurred in the underlying verify stream decoder; + * check FLAC__stream_encoder_get_verify_decoder_state(). + */ + + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + /**< The verify decoder detected a mismatch between the original + * audio signal and the decoded audio signal. + */ + + FLAC__STREAM_ENCODER_CLIENT_ERROR, + /**< One of the callbacks returned a fatal error. */ + + FLAC__STREAM_ENCODER_IO_ERROR, + /**< An I/O error occurred while opening/reading/writing a file. + * Check \c errno. + */ + + FLAC__STREAM_ENCODER_FRAMING_ERROR, + /**< An error occurred while writing the stream; usually, the + * write_callback returned an error. + */ + + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + /**< Memory allocation failed. */ + +} FLAC__StreamEncoderState; + +/** Maps a FLAC__StreamEncoderState to a C string. + * + * Using a FLAC__StreamEncoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderStateString[]; + + +/** Possible return values for the FLAC__stream_encoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + /**< General failure to set up encoder; call FLAC__stream_encoder_get_state() for cause. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + /**< The encoder has an invalid setting for number of channels. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + /**< The encoder has an invalid setting for bits-per-sample. + * FLAC supports 4-32 bps but the reference encoder currently supports + * only up to 24 bps. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + /**< The encoder has an invalid setting for the input sample rate. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + /**< The encoder has an invalid setting for the block size. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + /**< The encoder has an invalid setting for the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + /**< The encoder has an invalid setting for the precision of the quantized linear predictor coefficients. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + /**< The specified block size is less than the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + /**< The encoder is bound to the Subset but other settings violate it. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + /**< The metadata input to the encoder is invalid, in one of the following ways: + * - FLAC__stream_encoder_set_metadata() was called with a null pointer but a block count > 0 + * - One of the metadata blocks contains an undefined type + * - It contains an illegal CUESHEET as checked by FLAC__format_cuesheet_is_legal() + * - It contains an illegal SEEKTABLE as checked by FLAC__format_seektable_is_legal() + * - It contains more than one SEEKTABLE block or more than one VORBIS_COMMENT block + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_encoder_init_*() was called when the encoder was + * already initialized, usually because + * FLAC__stream_encoder_finish() was not called. + */ + +} FLAC__StreamEncoderInitStatus; + +/** Maps a FLAC__StreamEncoderInitStatus to a C string. + * + * Using a FLAC__StreamEncoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderInitStatusString[]; + + +/** Return values for the FLAC__StreamEncoder read callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted at the end of the stream. */ + + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED + /**< Client does not support reading back from the output. */ + +} FLAC__StreamEncoderReadStatus; + +/** Maps a FLAC__StreamEncoderReadStatus to a C string. + * + * Using a FLAC__StreamEncoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderReadStatusString[]; + + +/** Return values for the FLAC__StreamEncoder write callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + /**< The write was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__StreamEncoderWriteStatus; + +/** Maps a FLAC__StreamEncoderWriteStatus to a C string. + * + * Using a FLAC__StreamEncoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[]; + + +/** Return values for the FLAC__StreamEncoder seek callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderSeekStatus; + +/** Maps a FLAC__StreamEncoderSeekStatus to a C string. + * + * Using a FLAC__StreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamEncoder tell callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + /**< The tell was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderTellStatus; + +/** Maps a FLAC__StreamEncoderTellStatus to a C string. + * + * Using a FLAC__StreamEncoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderTellStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamEncoder + * + ***********************************************************************/ + +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +/** The opaque structure definition for the stream encoder type. + * See the \link flac_stream_encoder stream encoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamEncoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init_ogg_stream() if seeking is supported. + * The supplied function will be called when the encoder needs to read back + * encoded data. This happens during the metadata callback, when the encoder + * has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered + * while encoding. The address of the buffer to be filled is supplied, along + * with the number of bytes the buffer can hold. The callback may choose to + * supply less data and modify the byte count but must be careful not to + * overflow the buffer. The callback then returns a status code chosen from + * FLAC__StreamEncoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamEncoderReadStatus read_cb(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be encoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_set_client_data(). + * \retval FLAC__StreamEncoderReadStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * by the encoder anytime there is raw encoded data ready to write. It may + * include metadata mixed with encoded audio frames and the data is not + * guaranteed to be aligned on frame or metadata block boundaries. + * + * The only duty of the callback is to write out the \a bytes worth of data + * in \a buffer to the current position in the output stream. The arguments + * \a samples and \a current_frame are purely informational. If \a samples + * is greater than \c 0, then \a current_frame will hold the current frame + * number that is being written; otherwise it indicates that the write + * callback is being called to write metadata. + * + * \note + * Unlike when writing to native FLAC, when writing to Ogg FLAC the + * write callback will be called twice when writing each audio + * frame; once for the page header, and once for the page body. + * When writing the page header, the \a samples argument to the + * write callback will be \c 0. + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer An array of encoded data of length \a bytes. + * \param bytes The byte length of \a buffer. + * \param samples The number of samples encoded by \a buffer. + * \c 0 has a special meaning; see above. + * \param current_frame The number of the current frame being encoded. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to seek the output stream. The encoder will pass + * the absolute byte offset to seek to, 0 meaning the beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamEncoderSeekStatus seek_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to know the current position of the output stream. + * + * \warning + * The callback must return the true current byte offset of the output to + * which the encoder is writing. If you are buffering the output, make + * sure and take this into account. If you are writing directly to a + * FILE* from your write callback, ftell() is sufficient. If you are + * writing directly to a file descriptor from your write callback, you + * can use lseek(fd, SEEK_CUR, 0). The encoder may later seek back to + * these points to rewrite metadata after encoding. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamEncoderTellStatus tell_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The address at which to store the current + * position of the output. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * once at the end of encoding with the populated STREAMINFO structure. This + * is so the client can seek back to the beginning of the file and write the + * STREAMINFO block with the correct statistics after encoding (like + * minimum/maximum frame size and total samples). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param metadata The final populated STREAMINFO block. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the progress callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE(). + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the + * callback will be based on the value from + * FLAC__stream_encoder_set_total_samples_estimate(). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param bytes_written Bytes written so far. + * \param samples_written Samples written so far. + * \param frames_written Frames written so far. + * \param total_frames_estimate The estimate of the total number of + * frames to be written. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, uint32_t frames_written, uint32_t total_frames_estimate, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream encoder instance. The instance is created with + * default settings; see the individual FLAC__stream_encoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamEncoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void); + +/** Free an encoder instance. Deletes the object pointed to by \a encoder. + * + * \param encoder A pointer to an existing encoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream to use in the Ogg container. + * + * \note + * This does not need to be set for native FLAC encoding. + * + * \note + * It is recommended to set a serial number explicitly as the default of '0' + * may collide with other streams. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param serial_number See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long serial_number); + +/** Set the "verify" flag. If \c true, the encoder will verify it's own + * encoded output by feeding it through an internal decoder and comparing + * the original signal against the decoded signal. If a mismatch occurs, + * the process call will return \c false. Note that this will slow the + * encoding process by the extra time required for decoding and comparison. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the Subset flag. If \c true, + * the encoder will comply with the Subset and will check the + * settings during FLAC__stream_encoder_init_*() to see if all settings + * comply. If \c false, the settings may take advantage of the full + * range that the format allows. + * + * Make sure you know what it entails before setting this to \c false. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the number of channels to be encoded. + * + * \default \c 2 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set the sample resolution of the input to be encoded. + * + * \warning + * Do not feed the encoder data that is wider than the value you + * set here or you will generate an invalid stream. + * + * \default \c 16 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set the sample rate (in Hz) of the input to be encoded. + * + * \default \c 44100 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set the compression level + * + * The compression level is roughly proportional to the amount of effort + * the encoder expends to compress the file. A higher level usually + * means more computation but higher compression. The default level is + * suitable for most applications. + * + * Currently the levels range from \c 0 (fastest, least compression) to + * \c 8 (slowest, most compression). A value larger than \c 8 will be + * treated as \c 8. + * + * This function automatically calls the following other \c _set_ + * functions with appropriate values, so the client does not need to + * unless it specifically wants to override them: + * - FLAC__stream_encoder_set_do_mid_side_stereo() + * - FLAC__stream_encoder_set_loose_mid_side_stereo() + * - FLAC__stream_encoder_set_apodization() + * - FLAC__stream_encoder_set_max_lpc_order() + * - FLAC__stream_encoder_set_qlp_coeff_precision() + * - FLAC__stream_encoder_set_do_qlp_coeff_prec_search() + * - FLAC__stream_encoder_set_do_escape_coding() + * - FLAC__stream_encoder_set_do_exhaustive_model_search() + * - FLAC__stream_encoder_set_min_residual_partition_order() + * - FLAC__stream_encoder_set_max_residual_partition_order() + * - FLAC__stream_encoder_set_rice_parameter_search_dist() + * + * The actual values set for each level are: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
leveldo mid-side stereoloose mid-side stereoapodizationmax lpc orderqlp coeff precisionqlp coeff prec searchescape codingexhaustive model searchmin residual partition ordermax residual partition orderrice parameter search dist
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5);partial_tukey(2) 8 0 false false false 0 6 0
7 true false tukey(0.5);partial_tukey(2) 12 0 false false false 0 6 0
8 true false tukey(0.5);partial_tukey(2);punchout_tukey(3) 12 0 false false false 0 6 0
+ * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set the blocksize to use while encoding. + * + * The number of samples to use per frame. Use \c 0 to let the encoder + * estimate a blocksize; this is usually best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set to \c true to enable mid-side encoding on stereo input. The + * number of channels must be 2 for this to have any effect. Set to + * \c false to use only independent channel coding. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set to \c true to enable adaptive switching between mid-side and + * left-right encoding on stereo input. Set to \c false to use + * exhaustive searching. Setting this to \c true requires + * FLAC__stream_encoder_set_do_mid_side_stereo() to also be set to + * \c true in order to have any effect. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Sets the apodization function(s) the encoder will use when windowing + * audio data for LPC analysis. + * + * The \a specification is a plain ASCII string which specifies exactly + * which functions to use. There may be more than one (up to 32), + * separated by \c ';' characters. Some functions take one or more + * comma-separated arguments in parentheses. + * + * The available functions are \c bartlett, \c bartlett_hann, + * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, + * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, + * \c rectangle, \c triangle, \c tukey(P), \c partial_tukey(n[/ov[/P]]), + * \c punchout_tukey(n[/ov[/P]]), \c welch. + * + * For \c gauss(STDDEV), STDDEV specifies the standard deviation + * (0blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set the maximum partition order to search when coding the residual. + * This is used in tandem with + * FLAC__stream_encoder_set_min_residual_partition_order(). + * + * The partition order determines the context size in the residual. + * The context size will be approximately blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Deprecated. Setting this value has no effect. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, uint32_t value); + +/** Set an estimate of the total samples that will be encoded. + * This is merely an estimate and may be set to \c 0 if unknown. + * This value will be written to the STREAMINFO block before encoding, + * and can remove the need for the caller to rewrite the value later + * if the value is known before encoding. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +/** Set the metadata blocks to be emitted to the stream before encoding. + * A value of \c NULL, \c 0 implies no metadata; otherwise, supply an + * array of pointers to metadata blocks. The array is non-const since + * the encoder may need to change the \a is_last flag inside them, and + * in some cases update seek point offsets. Otherwise, the encoder will + * not modify or free the blocks. It is up to the caller to free the + * metadata blocks after encoding finishes. + * + * \note + * The encoder stores only copies of the pointers in the \a metadata array; + * the metadata blocks themselves must survive at least until after + * FLAC__stream_encoder_finish() returns. Do not free the blocks until then. + * + * \note + * The STREAMINFO block is always written and no STREAMINFO block may + * occur in the supplied array. + * + * \note + * By default the encoder does not create a SEEKTABLE. If one is supplied + * in the \a metadata array, but the client has specified that it does not + * support seeking, then the SEEKTABLE will be written verbatim. However + * by itself this is not very useful as the client will not know the stream + * offsets for the seekpoints ahead of time. In order to get a proper + * seektable the client must support seeking. See next note. + * + * \note + * SEEKTABLE blocks are handled specially. Since you will not know + * the values for the seek point stream offsets, you should pass in + * a SEEKTABLE 'template', that is, a SEEKTABLE object with the + * required sample numbers (or placeholder points), with \c 0 for the + * \a frame_samples and \a stream_offset fields for each point. If the + * client has specified that it supports seeking by providing a seek + * callback to FLAC__stream_encoder_init_stream() or both seek AND read + * callback to FLAC__stream_encoder_init_ogg_stream() (or by using + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE()), + * then while it is encoding the encoder will fill the stream offsets in + * for you and when encoding is finished, it will seek back and write the + * real values into the SEEKTABLE block in the stream. There are helper + * routines for manipulating seektable template blocks; see metadata.h: + * FLAC__metadata_object_seektable_template_*(). If the client does + * not support seeking, the SEEKTABLE will have inaccurate offsets which + * will slow down or remove the ability to seek in the FLAC stream. + * + * \note + * The encoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * + * \note + * A VORBIS_COMMENT block may be supplied. The vendor string in it + * will be ignored. libFLAC will use it's own vendor string. libFLAC + * will not modify the passed-in VORBIS_COMMENT's vendor string, it + * will simply write it's own into the stream. If no VORBIS_COMMENT + * block is present in the \a metadata array, libFLAC will write an + * empty one, containing only the vendor string. + * + * \note The Ogg FLAC mapping requires that the VORBIS_COMMENT block be + * the second metadata block of the stream. The encoder already supplies + * the STREAMINFO block automatically. If \a metadata does not contain a + * VORBIS_COMMENT block, the encoder will supply that too. Otherwise, if + * \a metadata does contain a VORBIS_COMMENT block and it is not the + * first, the init function will reorder \a metadata by moving the + * VORBIS_COMMENT block to the front; the relative ordering of the other + * blocks will remain as they were. + * + * \note The Ogg FLAC mapping limits the number of metadata blocks per + * stream to \c 65535. If \a num_blocks exceeds this the function will + * return \c false. + * + * \default \c NULL, 0 + * \param encoder An encoder instance to set. + * \param metadata See above. + * \param num_blocks See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + * \c false if the encoder is already initialized, or if + * \a num_blocks > 65535 if encoding to Ogg FLAC, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, uint32_t num_blocks); + +/** Get the current encoder state. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamEncoderState + * The current encoder state. + */ +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); + +/** Get the state of the verify stream decoder. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The verify stream decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder); + +/** Get the current encoder state as a C string. + * This version automatically resolves + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR by getting the + * verify decoder's state. + * + * \param encoder A encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval const char * + * The encoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); + +/** Get relevant values about the nature of a verify decoder error. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. The arguments should + * be addresses in which the stats will be returned, or NULL if value + * is not desired. + * + * \param encoder An encoder instance to query. + * \param absolute_sample The absolute sample number of the mismatch. + * \param frame_number The number of the frame in which the mismatch occurred. + * \param channel The channel in which the mismatch occurred. + * \param sample The number of the sample (relative to the frame) in + * which the mismatch occurred. + * \param expected The expected value for the sample in question. + * \param got The actual value returned by the decoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, uint32_t *frame_number, uint32_t *channel, uint32_t *sample, FLAC__int32 *expected, FLAC__int32 *got); + +/** Get the "verify" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_verify(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); + +/** Get the frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of pointers to each channel's signal. + * \param samples The number of samples in one channel. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], uint32_t samples); + +/** Submit data for encoding. + * This version allows you to supply the input data where the channels + * are interleaved into a single array (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * The samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 + * and the last value channelN_sampleM. Each sample should be a signed + * integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the + * resolution is 16 bits per sample, the samples should all be in the + * range [-32768,32767]. + * + * For applications where channel order is important, channels must + * follow the order as described in the + * frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of channel-interleaved data (see above). + * \param samples The number of samples in one channel, the same as for + * FLAC__stream_encoder_process(). For example, if + * encoding two channels, \c 1000 \a samples corresponds + * to a \a buffer of 2000 values. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], uint32_t samples); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pyflac/libraries/LICENSE b/pyflac/libraries/LICENSE new file mode 100644 index 0000000..d8295f0 --- /dev/null +++ b/pyflac/libraries/LICENSE @@ -0,0 +1,29 @@ +Copyright (C) 2000-2009 Josh Coalson +Copyright (C) 2011-2016 Xiph.Org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pyflac/libraries/darwin-x86_64/libFLAC.8.dylib b/pyflac/libraries/darwin-x86_64/libFLAC.8.dylib new file mode 100644 index 0000000..06dd75a Binary files /dev/null and b/pyflac/libraries/darwin-x86_64/libFLAC.8.dylib differ diff --git a/pyflac/libraries/linux-x86_64/libFLAC-8.3.0.so b/pyflac/libraries/linux-x86_64/libFLAC-8.3.0.so new file mode 100644 index 0000000..02f9aa7 Binary files /dev/null and b/pyflac/libraries/linux-x86_64/libFLAC-8.3.0.so differ diff --git a/pyflac/libraries/raspbian-armv6z/libFLAC-8.3.0.so b/pyflac/libraries/raspbian-armv6z/libFLAC-8.3.0.so new file mode 100644 index 0000000..b7d326e Binary files /dev/null and b/pyflac/libraries/raspbian-armv6z/libFLAC-8.3.0.so differ diff --git a/pyflac/libraries/raspbian-armv7a/libFLAC-8.3.0.so b/pyflac/libraries/raspbian-armv7a/libFLAC-8.3.0.so new file mode 100644 index 0000000..b730034 Binary files /dev/null and b/pyflac/libraries/raspbian-armv7a/libFLAC-8.3.0.so differ diff --git a/pyflac/libraries/windows-i686/FLAC-8.lib b/pyflac/libraries/windows-i686/FLAC-8.lib new file mode 100644 index 0000000..3c2be8a Binary files /dev/null and b/pyflac/libraries/windows-i686/FLAC-8.lib differ diff --git a/pyflac/libraries/windows-i686/libFLAC-8.dll b/pyflac/libraries/windows-i686/libFLAC-8.dll new file mode 100644 index 0000000..9c0893d Binary files /dev/null and b/pyflac/libraries/windows-i686/libFLAC-8.dll differ diff --git a/pyflac/libraries/windows-x86_64/FLAC-8.lib b/pyflac/libraries/windows-x86_64/FLAC-8.lib new file mode 100644 index 0000000..e3dcab1 Binary files /dev/null and b/pyflac/libraries/windows-x86_64/FLAC-8.lib differ diff --git a/pyflac/libraries/windows-x86_64/libFLAC-8.dll b/pyflac/libraries/windows-x86_64/libFLAC-8.dll new file mode 100644 index 0000000..4e78118 Binary files /dev/null and b/pyflac/libraries/windows-x86_64/libFLAC-8.dll differ diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9692d51 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,13 @@ +[metadata] +description-file = README.rst + +[build_sphinx] +source-dir = docs +build-dir = docs/_build +all_files = 1 + +[upload_sphinx] +upload-dir = docs/_build/html + +[flake8] +max-line-length = 120 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..042bfb4 --- /dev/null +++ b/setup.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import os +import re +from setuptools import setup, find_packages + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +vstr = read('pyflac/__init__.py') +regex = r"^__version__ = ['\"]([^'\"]*)['\"]" +version = re.search(regex, vstr, re.M) +__version__ = version.group(1) + +setup( + name='pyFLAC', + version=__version__, + description='A Python wrapper for libFLAC', + long_description=open('README.rst').read(), + long_description_content_type='text/x-rst', + author='Sonos, Inc', + author_email='joe.todd@sonos.com', + license='Apache License 2.0', + url='http://pyflac.readthedocs.io/en/latest/', + download_url='https://github.com/sonos/pyFLAC/archive/' + __version__ + '.tar.gz', + packages=find_packages(), + include_package_data=True, + setup_requires=['cffi>=1.4.0'], + cffi_modules=[ + 'pyflac/builder/encoder.py:ffibuilder', + 'pyflac/builder/decoder.py:ffibuilder' + ], + install_requires=[ + 'cffi>=1.4.0', + 'numpy; python_version >= "3.7.0"', + 'numpy<=1.19.5; python_version < "3.7.0"', + 'SoundFile>=0.8.0', + ], + test_suite='tests', + python_requires='>=3.6', + entry_points={ + 'console_scripts': [ + 'pyflac = pyflac.__main__:main', + ], + }, + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Topic :: Multimedia :: Sound/Audio', + ], +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/8bit.flac b/tests/data/8bit.flac new file mode 100644 index 0000000..c0aebfa Binary files /dev/null and b/tests/data/8bit.flac differ diff --git a/tests/data/LICENSE b/tests/data/LICENSE new file mode 100644 index 0000000..feedd80 --- /dev/null +++ b/tests/data/LICENSE @@ -0,0 +1 @@ +All test audio is from freesound.org licensed under the Creative Commons 0 License. diff --git a/tests/data/mono.flac b/tests/data/mono.flac new file mode 100644 index 0000000..40aca00 Binary files /dev/null and b/tests/data/mono.flac differ diff --git a/tests/data/mono.wav b/tests/data/mono.wav new file mode 100644 index 0000000..caf276c Binary files /dev/null and b/tests/data/mono.wav differ diff --git a/tests/data/stereo.flac b/tests/data/stereo.flac new file mode 100644 index 0000000..8ea7ac4 Binary files /dev/null and b/tests/data/stereo.flac differ diff --git a/tests/data/stereo.wav b/tests/data/stereo.wav new file mode 100644 index 0000000..6961259 Binary files /dev/null and b/tests/data/stereo.wav differ diff --git a/tests/data/surround.flac b/tests/data/surround.flac new file mode 100644 index 0000000..326ad95 Binary files /dev/null and b/tests/data/surround.flac differ diff --git a/tests/data/surround.wav b/tests/data/surround.wav new file mode 100644 index 0000000..0e38025 Binary files /dev/null and b/tests/data/surround.wav differ diff --git a/tests/test_decoder.py b/tests/test_decoder.py new file mode 100644 index 0000000..0be5cea --- /dev/null +++ b/tests/test_decoder.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC decoder test suite +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import os +import pathlib +import tempfile +import time +import unittest + +import numpy as np +from pyflac.decoder import _Decoder +from pyflac import ( + FileDecoder, + StreamDecoder, + DecoderState, + DecoderInitException, + DecoderProcessException +) + + +class TestDecoder(unittest.TestCase): + """ + Test Suite for the generic decoder class + """ + def setUp(self): + self.decoder = _Decoder() + + def test_state(self): + """ Test that the state returns a valid string """ + self.assertEqual(self.decoder.state, DecoderState.UNINITIALIZED) + self.assertEqual(str(self.decoder.state), 'FLAC__STREAM_DECODER_UNINITIALIZED') + + +class TestStreamDecoder(unittest.TestCase): + """ + Test suite for the stream decoder class. + """ + def setUp(self): + self.decoder = None + self.callback_called = False + self.tests_path = pathlib.Path(__file__).parent.absolute() + + def _callback(self, data, rate, channels, samples): + assert isinstance(data, np.ndarray) + assert isinstance(rate, int) + assert isinstance(channels, int) + assert isinstance(samples, int) + self.callback_called = True + + def test_process_invalid_data(self): + """ Test that processing invalid data raises an exception """ + test_data = bytearray(os.urandom(100000)) + + with self.assertRaises(DecoderProcessException): + self.decoder = StreamDecoder(callback=self._callback) + self.decoder.process(test_data) + self.decoder.finish() + + def test_process(self): + """ Test that FLAC data can be decoded """ + test_path = self.tests_path / 'data/stereo.flac' + with open(test_path, 'rb') as flac: + test_data = flac.read() + + self.decoder = StreamDecoder(callback=self._callback) + time.sleep(0.05) + + self.decoder.process(test_data) + self.decoder.finish() + self.assertTrue(self.callback_called) + + def test_process_blocks(self): + """ Test that FLAC data can be decoded in blocks """ + blocksize = 1024 + test_path = self.tests_path / 'data/stereo.flac' + with open(test_path, 'rb') as flac: + test_data = flac.read() + data_length = len(test_data) + + self.decoder = StreamDecoder(callback=self._callback) + for i in range(0, data_length - blocksize, blocksize): + self.decoder.process(test_data[i:i + blocksize]) + + self.decoder._done = True + + +class TestFileDecoder(unittest.TestCase): + """ + Test suite for the file decoder class. + """ + def setUp(self): + self.decoder = None + self.callback_called = False + self.temp_file = tempfile.NamedTemporaryFile(suffix='.wav') + self.default_kwargs = {'input_file': None} + + def test_process_invalid_file(self): + """ Test that an invalid file raises an error """ + self.default_kwargs['input_file'] = pathlib.Path('invalid.flac') + with self.assertRaises(DecoderInitException): + self.decoder = FileDecoder(**self.default_kwargs) + + def test_process_8bit_file(self): + """ Test that an 8bit file raises an error """ + test_file = pathlib.Path(__file__).parent / 'data/8bit.flac' + self.default_kwargs['input_file'] = test_file + with self.assertRaises(DecoderProcessException): + self.decoder = FileDecoder(**self.default_kwargs) + self.decoder.process() + + def test_process_mono_file(self): + """ Test that a mono FLAC file can be processed """ + test_file = pathlib.Path(__file__).parent / 'data/mono.flac' + self.default_kwargs['input_file'] = test_file + self.decoder = FileDecoder(**self.default_kwargs) + self.assertIsNotNone(self.decoder.process()) + + def test_process_stereo_file(self): + """ Test that a stereo FLAC file can be processed """ + test_file = pathlib.Path(__file__).parent / 'data/stereo.flac' + self.default_kwargs['input_file'] = test_file + self.decoder = FileDecoder(**self.default_kwargs) + self.assertIsNotNone(self.decoder.process()) + + def test_process_5_1_surround_file(self): + """ Test that a 5.1 surround FLAC file can be processed """ + test_file = pathlib.Path(__file__).parent / 'data/surround.flac' + self.default_kwargs['input_file'] = test_file + self.default_kwargs['output_file'] = pathlib.Path(self.temp_file.name) + self.decoder = FileDecoder(**self.default_kwargs) + self.assertIsNotNone(self.decoder.process()) + + +if __name__ == '__main__': + unittest.main(failfast=True) diff --git a/tests/test_encoder.py b/tests/test_encoder.py new file mode 100644 index 0000000..8f5dd71 --- /dev/null +++ b/tests/test_encoder.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# +# pyFLAC encoder test suite +# +# Copyright (c) 2011-2021, Sonos, Inc. +# All rights reserved. +# +# ------------------------------------------------------------------------------ + +import pathlib +import tempfile +import unittest + +import numpy as np +import soundfile as sf +from pyflac.encoder import _Encoder +from pyflac import ( + StreamEncoder, + FileEncoder, + EncoderState, + EncoderInitException, +) + +DEFAULT_CHANNELS = 1 +DEFAULT_SAMPLE_RATE = 44100 +DEFAULT_BITS_PER_SAMPLE = 16 +DEFAULT_BLOCKSIZE = 1024 + + +class TestEncoder(unittest.TestCase): + """ + Test Suite for the generic encoder class + """ + def setUp(self): + self.encoder = _Encoder() + + def test_verify(self): + """ Test that the verify setter returns the same value from libFLAC """ + self.encoder._verify = True + self.assertTrue(self.encoder._verify) + + def test_channels(self): + """ Test that the channels setter returns the same value from libFLAC """ + test_channels = 2 + self.encoder._channels = test_channels + self.assertEqual(self.encoder._channels, test_channels) + + def test_bits_per_sample(self): + """ Test that the bits_per_sample setter returns the same value from libFLAC """ + test_bits_per_sample = 24 + self.encoder._bits_per_sample = test_bits_per_sample + self.assertEqual(self.encoder._bits_per_sample, test_bits_per_sample) + + def test_sample_rate(self): + """ Test that the sample_rate setter returns the same value from libFLAC """ + test_sample_rate = 48000 + self.encoder._sample_rate = test_sample_rate + self.assertEqual(self.encoder._sample_rate, test_sample_rate) + + def test_blocksize(self): + """ Test that the blocksize setter returns the same value from libFLAC """ + test_blocksize = 128 + self.encoder._blocksize = test_blocksize + self.assertEqual(self.encoder._blocksize, test_blocksize) + + def test_compression_level(self): + """ Test that the compression level setter returns the same value from libFLAC """ + test_compression_level = 8 + self.encoder._compression_level = test_compression_level + + def test_state(self): + """ Test that the state returns uninitialised """ + self.assertEqual(self.encoder.state, EncoderState.UNINITIALIZED) + self.assertEqual(str(self.encoder.state), 'FLAC__STREAM_ENCODER_UNINITIALIZED') + + def test_invalid_process(self): + """ Test that an error is returned if a numpy array is not provided """ + with self.assertRaises(TypeError): + self.encoder.process([1, 2, 3, 4]) + + +class TestStreamEncoder(unittest.TestCase): + """ + Test suite for the stream encoder class + """ + def setUp(self): + self.callback_called = False + self.encoder = None + self.default_kwargs = { + 'sample_rate': DEFAULT_SAMPLE_RATE, + 'blocksize': DEFAULT_BLOCKSIZE, + 'callback': self._callback, + 'verify': True + } + + def tearDown(self): + if self.encoder: + self.encoder.finish() + + def _callback(self, + buffer: bytes, + num_bytes: int, + num_samples: int, + current_frame: int): + assert isinstance(buffer, bytes) + assert isinstance(num_bytes, int) + assert isinstance(num_samples, int) + assert isinstance(current_frame, int) + self.callback_called = True + + def test_invalid_sample_rate(self): + """ Test than an exception is raised if given an invalid sample rate """ + self.encoder = StreamEncoder(sample_rate=1000000, callback=self._callback) + with self.assertRaises(EncoderInitException) as err: + self.encoder._init() + self.assertEqual(str(err), 'FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE') + + def test_invalid_blocksize(self): + """ Test than an exception is raised if given an invalid block size """ + self.encoder = StreamEncoder( + sample_rate=DEFAULT_SAMPLE_RATE, + blocksize=1000000, + callback=self._callback + ) + with self.assertRaises(EncoderInitException) as err: + self.encoder._init() + self.assertEqual(str(err), 'FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE') + + def test_process_mono(self): + """ Test that an array of int16 mono samples can be processed """ + self.encoder = StreamEncoder(**self.default_kwargs) + test_samples = np.random.rand(DEFAULT_BLOCKSIZE, 1).astype('int16') + self.encoder.process(test_samples) + self.assertTrue(self.callback_called) + + def test_process_stereo(self): + """ Test that an array of int16 stereo samples can be processed """ + self.encoder = StreamEncoder(**self.default_kwargs) + test_samples = np.random.rand(DEFAULT_BLOCKSIZE, 2).astype('int16') + self.encoder.process(test_samples) + self.assertTrue(self.callback_called) + + +class TestFileEncoder(unittest.TestCase): + """ + Test Suite for the file encoder class. + """ + def setUp(self): + self.encoder = None + self.test_file = pathlib.Path(__file__).parent.absolute() / 'data/mono.wav' + self.temp_file = tempfile.NamedTemporaryFile(suffix='.flac') + self.default_kwargs = { + 'input_file': self.test_file, + 'blocksize': DEFAULT_BLOCKSIZE, + 'verify': True + } + + def test_invalid_blocksize(self): + """ Test than an exception is raised if given an invalid block size """ + self.default_kwargs['blocksize'] = 1000000 + self.encoder = FileEncoder(**self.default_kwargs) + with self.assertRaises(EncoderInitException): + self.encoder._init() + + def test_state(self): + """ Test that the initial state is ok """ + self.encoder = FileEncoder(**self.default_kwargs) + self.assertEqual(self.encoder.state, EncoderState.UNINITIALIZED) + + def test_process_mono_file(self): + """ Test that a mono WAV file can be processed """ + test_path = pathlib.Path(__file__).parent.absolute() / 'data/mono.wav' + self.default_kwargs['input_file'] = test_path + self.default_kwargs['output_file'] = pathlib.Path(self.temp_file.name) + self.encoder = FileEncoder(**self.default_kwargs) + self.encoder.process() + + def test_process_stereo_file(self): + """ Test that a stereo WAV file can be processed """ + test_path = pathlib.Path(__file__).parent.absolute() / 'data/stereo.wav' + self.default_kwargs['input_file'] = test_path + self.encoder = FileEncoder(**self.default_kwargs) + self.encoder.process() + + def test_process_5_1_surround_file(self): + """ Test that a 5.1 surround WAV file can be processed """ + test_path = pathlib.Path(__file__).parent.absolute() / 'data/surround.wav' + self.default_kwargs['input_file'] = test_path + self.encoder = FileEncoder(**self.default_kwargs) + self.encoder.process() + + +if __name__ == '__main__': + unittest.main(failfast=True) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..c9dc4c2 --- /dev/null +++ b/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py36, py37, py38, py39 + +[testenv] +deps = pytest==6.2.2 + pytest-cov==2.11.1 +commands = pytest --cov=. --cov-config .coveragerc --cov-report=xml --maxfail=1